- A+
TS官方手册:TypeScript: Handbook - The TypeScript Handbook (typescriptlang.org)
匿名与具名
对象类型的声明可以是匿名的,也可以使用interface
或type
进行具名声明。
function greet(person: { name: string; age: number }) { return "Hello " + person.name; }
interface Person { name: string; age: number; } function greet(person: Person) { return "Hello " + person.name; }
type Person = { name: string; age: number; }; function greet(person: Person) { return "Hello " + person.name; }
可选属性 optional
使用?
标记:
interface PaintOptions { shape: Shape; xPos?: number; yPos?: number; }
注:使用PaintOptions
声明的对象,它的xPos
属性会被初步推断为number | undefined
类型。
可以使用条件语句或者解构+默认值的方式收束类型,排除undefined
的情况:
function paintShape(opts: PaintOptions) { let xPos = opts.xPos === undefined ? 0 : opts.xPos; // xPos 的类型为number }
function paintShape({ shape, xPos = 0, yPos = 0 }: PaintOptions) { // xPos 的类型为number,因为undefined会被默认值0取代 console.log("x coordinate at", xPos); }
只读属性 readonly
interface SomeType { readonly prop: string; }
注:如果有一个属性的值是引用值,例如一个对象或数组,且这个属性被标记为只读(readonly),我们不能修改它的引用值,但是可以修改它的内部属性。
interface Home { readonly resident: { name: string; age: number }; } function visitForBirthday(home: Home) { // 我们可以读取并且更新'home.resident'里的属性 console.log(`Happy birthday ${home.resident.name}!`); home.resident.age++; } function evict(home: Home) { // 但我们不能更新'home.resident'本身 home.resident = { Cannot assign to 'resident' because it is a read-only property. name: "Victor the Evictor", age: 42, }; }
索引签名 index signatures
interface StringArray { [index: number]: string; } const myArray: StringArray = getStringArray(); const secondItem = myArray[1];
索引的类型只能是:string
,number
,symbol
,以及与这些类型相关的联合类型。
通常只会考虑string
和number
类型的索引。
注:可以同时支持string
,number
两种类型的索引器,但数字索引器返回的类型必须是字符串索引器返回类型的子类型。这是因为当使用数字进行索引时,JS 实际上会在索引到对象之前将其转换为字符串。于是使用100
和"100"
进行索引的结果是一样的。
当指定string类型索引的类型为S后,所有属性的类型都需要是S的子集。
interface NumberDictionary { [index: string]: number; length: number; // ok name: string; // NOT ok }
这是因为当我们访问obj.a
的时候,其实也是在访问obj["a"]
。
在上面这个例子中,string
类型索引被声明为number
类型,这意味着这个对象的所有属性都是number
类型,而下方name
却被声明为string
类型,矛盾了。
可以使用联合类型解决这个问题:
interface NumberDictionary { [index: string]: number | string; length: number; // ok name: string; // ok }
同时,索引签名也可以设置为只读:
interface ReadonlyStringArray { readonly [index: number]: string; }
类型继承
使用extends
,支持多继承:
interface Colorful { color: string; } interface Circle { radius: number; } interface ColorfulCircle extends Colorful, Circle {} const cc: ColorfulCircle = { color: "red", radius: 42, };
交集类型 intersection types
使用&
运算符获取已知的两个类型的交集。
interface Colorful { color: string; } interface Circle { radius: number; } type ColorfulCircle = Colorful & Circle;
将两个基本数据类型取交集会得到never
。
类型继承 VS 交集类型
二者都可以产生更复杂的类型。
前者是在原有的类型的基础上添加可能未有的属性形成新属性,而后者将已有的类型进行组合。
泛型对象类型 Generic Object Types
interface Box<Type> { contents: Type; } let box: Box<string>;
也可以与泛型函数结合使用:
function setContents<Type>(box: Box<Type>, newContents: Type) { box.contents = newContents; }
除了使用interface
,也可以使用type
别名定义泛型类型。interface
一般用来声明对象类型,而type
更灵活,可以组合多种类型:
type OrNull<Type> = Type | null; type OneOrMany<Type> = Type | Type[]; // type OneOrManyOrNull<Type> = OneOrMany<Type> | null type OneOrManyOrNull<Type> = OrNull<OneOrMany<Type>>; // type OneOrManyOrNullStrings = OneOrMany<string> | null type OneOrManyOrNullStrings = OneOrManyOrNull<string>;