TypeScirpt 中文教程之Object----翻译自ts官方


JS 中最常用的数据形式莫过于对象了。TS 中也有对应的类型 object type.

function greet(person: {name: string; age: number}) {...}

或者用接口 interface 定义对象类型

interface Person {
  name: string;
  age: number;

}

function greet(person: Person) {...}

还可以使用 type 别名定义

type Person = {
  name: string;
  age: number;
}

至于使用接口还是别名 ,移步

以上我们编写了一个函数,这个函数接收一个对象参数,且属性 name 必须为 string, 属性 age 必须为 number。


?? 属性修饰符:可选

可选参数,之前也介绍了:在属性名后紧跟一个 ? 即可。使用可选参数后,如果不传入一个可选参数,访问时则为 undefined 。

我们结合解构赋值,达到默认值的效果

interface PaintOptions {   shape: Shape;   xPos?: number;   yPos?: number; }   function paintShape({ shape, xPos = 0, yPos = 0 }: PaintOptions) {   console.log("x coordinate at", xPos);                   console.log("y coordinate at", yPos); }   ?? 属性修饰符:readonly 只读   1. 属性可以标记为 readonly 只读。这里的只读,只是 TS 检查中标记不可被重写的语法,并不会强制只读。   interface SomeType {   readonly prop: string; }
function doSomething(obj: SomeType) {   // 读取属性   console.log(`prop has the value ${obj.prop}.`);
  // 重写会有错误提醒,但不强制   obj.prop = "hello";   console.log(obj.prop) }
doSomething({prop: 'Bowen'})  // 会打印出什么呢?

2. 对于引用类型的值TS并不会提示 ’只读‘,这就好比 const obj = {prop: value},你是可以修改prop的值,但是不可以给 obj 重新赋值

interface Home {   readonly resident: { name: string; age: number }; }
function visitForBirthday(home: Home) {   // 读取并修改对象内的属性   console.log(`Happy birthday ${home.resident.name}!`);   home.resident.age++; }
function evict(home: Home) {   // 不能重新赋值   home.resident = {     name: "Victor the Evictor",     age: 42,   }; }   ?? 索引签名   当你不知道对象中某个属性名称,但是知道属性名称的类型时,可是使用索引签名描述属性名称的类型:    interface StringArray {
  [index: number]: string;
} const myArray: StringArray = getStringArray();
const secondItem = myArray[1];   其中 index 不是固定形式,你可以使用任何表意明确的字符表示。举一个常见的例子:   interface UnkonwKeyName {   [a: string]: object,   [b: symbol]: number }
const sy = Symbol(); const obj: UnkonwKeyName = {   aString: {},   [sy]: 123 }   如果你定义的一个接口中既有未知属性名称又有明确的属性名称时,一定要做好兼容:下面是一个反例: interface NumberDictionary {
   [index: string]: number;    length: number;  // ??    name: string; //?? 这里必须是 number }   兼容修正:使用类型的联合声明修正 interface NumberOrStringDictionary {    [index: string]: number | string;    length: number; // ??number    name: string; // ??string }   ?? 泛型对象类型   在没有使用泛型之前,我们定义一个可以接受多类型的对象会用到 any,例如:
interface Box {
  contents: any;
}   但是 any 会引起类型不确定导致的问题,如类型缺少某种方法。 也可以使用 unknown 配合类型判断。   interface Box {
  contents: unknown;
} let x: Box  = {
  contents: '你好‘
} // 类型判断:
if (typeof x.contents === 'string') {
  console.log(x.contents.toLowerCase())
}   如果你不想做这样的判断,就需要按照不同类型创建不同的接口或者别名,
interface NumberBox {   contents: number } interface StringBox {   contents: string } ... 这种方式是安全可靠的,但不利于拓展,而且还会影响到类型声明,导致不得不采用多个函数或者重载去实现多类型的操作:   function setContents(box: NumberBox, newContents: number): void;
function setContents(box: StringBox, newContents: string): void; function setContents(box: { contents: any }, newContents: any) {
  box.contents = newContents
}   如此编写最大问题在于定义类型和调用时都重复编写了类型声明。   下面使用泛型对象解决这个问题:
interface Box {
 contents: Type;
}   其中尖括号中的Type可以放入任何类型,可以看作是其他类型的占位值:   let box: Box = { contents: '你好' };
let box1: Box = { contents: 123 };   对象的泛型配合函数的泛型可以达到很好的效果,避免使用函数重载:
function setContents(box: Box, newContents: Type) {
 box.contents = newContents;
}   上面都是基于接口 interface 来编写泛型的,其实你也可以用 类型别名定义泛型:
type Box = {   contents: Type; };   由于类型别名与接口不同,它可以描述的不仅仅是对象类型,我们也可以使用它们来编写其他类型的通用辅助类型   type OrNull = Type | null; type OneOrMany = Type | Type[];   ?? 使用泛型声明数组   声明一个数组可以用类型 + [] 的形式,也可以使用泛型数组,如下:
普通类型方式:let fruits: string[] = ['apple', 'Orange', 'Banana']; 泛型数组方式:let fruits: Array = ['apple', 'Orange', 'Banana'];   泛型数组也有一个只读的泛类型:ReadonlyArray<>   泛型数组只是声明数组类型的方式,不能用于创建一个数组实例:
new ReadonlyArray('red', 'green', 'blue'); // ?? 正确的用法: const roArray: ReadonlyArray = ["red", "green", "blue"]; // ??   ?? 元组类型   元组是一种已知了元素个数的数组,所以声明一个元组,需要明确指定包含元素的个数和类型:
type StringNumberPair = [string, number];   可以配合解构使用元组:
function doSomething(stringHash: [string, number]) {
  const [inputString, hash] = stringHash;   console.log(inputString);   console.log(hash);
}