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
let box1: Box
function setContents
box.contents = newContents;
} 上面都是基于接口 interface 来编写泛型的,其实你也可以用 类型别名定义泛型:
type Box
普通类型方式:let fruits: string[] = ['apple', 'Orange', 'Banana']; 泛型数组方式:let fruits: Array
new ReadonlyArray('red', 'green', 'blue'); // ?? 正确的用法: const roArray: ReadonlyArray
type StringNumberPair = [string, number]; 可以配合解构使用元组:
function doSomething(stringHash: [string, number]) {
const [inputString, hash] = stringHash; console.log(inputString); console.log(hash);
}