简言
在 JavaScript 中,我们分组和传递数据的基本方式是通过对象。在 TypeScript 中,我们通过对象类型来表示这些对象。
对象类型
在 JavaScript 中,我们分组和传递数据的基本方式是通过对象。在 TypeScript 中,我们通过对象类型来表示这些对象。
正如我们所看到的,它们可以是匿名的:
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;
}
这三种方式都挺常见的,一般都是使用后两种,使用接口或别名来定义类型,简单明了,且易复用。
对象属性类型
对象类型中的每个属性都可以指定几件事:类型、属性是否可选以及属性是否可以写入,以及属性值。
定义规则如下:
- 属性类型 – 一般是字符串类型的,如果想定义其他的类型,用方括号包裹定义属性索引修改。
- 属性值 – 和普通的变量类型定义一样,如果提前定义好了属性值,则需要符合提前定义好的类型范围。
- 可选属性 — 问号(?)定义。
- 仅读属性 — 属性默认是可写入的,仅读属性需要 索引前面加 readonly 关键词。
示例:
interface Person {name: string;age: number;
}
type Shape = { kind: "circle" } | { kind: "square" };
interface PaintOptions {shape: Shape;xPos?: number;yPos?: number;
}interface ReadonlyPerson {readonly name: string;readonly age: number;
}let writablePerson: Person = {name: "Person McPersonface",age: 42,
};// works
let readonlyPerson: ReadonlyPerson = writablePerson;console.log(readonlyPerson.age); // prints '42'
writablePerson.age++;
console.log(readonlyPerson.age); // prints '43'interface StringArray {[index: number]: string;
}const myArray: StringArray = ['1','2'];
const secondItem = myArray[1];
对象索引类型
使用方括号包含索引index属性定义。
interface NumberOrStringDictionary {[index: string]: number | string;length: number; // ok, length is a numbername: string; // ok, name is a string
}
上面提前定义了属性索引类型和属性值类型,不好扩展,也可以不指定属性值,即将属性值定义为any(任何类型):
interface SquareConfig {[index: string]: any;color?: string;width?: number;
}
可以在索引属性前加readonly,这样属性值不可修改:
interface ReadonlyStringArray {readonly [index: number]: string;
}let myArray: ReadonlyStringArray = getReadOnlyStringArray();
myArray[2] = "Mallory"; // 报错
接口继承
使用接口定义的对象类型,可以使用关键词extends来继承其他接口。
interface Colorful {color: string;
}interface Circle {radius: number;
}interface ColorfulCircle extends Colorful, Circle {}const cc: ColorfulCircle = {color: "red",radius: 42,
};
类型组合
接口允许我们通过扩展其他类型来创建新类型。TypeScript 提供了另一种称为交叉类型的结构,主要用于组合现有的对象类型。
组合类型是使用 & 运算符定义的。
interface Colorful {color: string;
}
interface Circle {radius: number;
}type ColorfulCircle = Colorful & Circle;type c1 = Colorful
type c2 = Circletype c3 = c1 & c2 // Colorful & Circle
类似于数学中的并集运算。
泛型对象类型
可以使用泛型,来定义通用的对象类型。
interface Box<Type> {contents: Type;
}
// 别名也可以使用泛型
type OrNull<Type> = Type | null;type OneOrMany<Type> = Type | Type[];type OneOrManyOrNull<Type> = OrNull<OneOrMany<Type>>;
使用时,指定Type具体的类型值,其contents属性即可动态改变。
let box: Box<string>;
将 Box 视为真实类型的模板,其中 Type 是一个占位符,将被其他类型替换。当 TypeScript 看到 Box<string> 时,它会用 string 替换 Box<Type> 中 Type 的每一个实例,并最终使用 { contents: string } 这样的代码。
元组对象类型
元组有的时候在表示列表对象类型时特别适合。
function doSomething(stringHash: [string, number]) {const [inputString, hash] = stringHash;console.log(inputString);console.log(hash);
}
元组也可以使用剩余元素,但必须是数组/元组类型。
type StringNumberBooleans = [string, number, ...boolean[]];
type StringBooleansNumber = [string, ...boolean[], number];
type BooleansStringNumber = [...boolean[], string, number];
为什么可选元素和剩余元素会有用?因为它允许 TypeScript 将元组与参数列表对应起来。元组类型可用于其余参数和参数,因此:
function readButtonInput(...args: [string, number, ...boolean[]]) {const [name, version, ...input] = args;// ...
}
// 相当于
function readButtonInput2(name: string, version: number, ...input: boolean[]) {// ...
}
结语
结束了。