對象類型通常使用interface聲明,可以設置屬性為可選的或者只讀的,可以設置索引簽名。從簡單類型生成複雜類型可以使用類型繼承或者交集類型。提高類型的泛用性可以使用泛型。 ...
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>;