持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第4天,点击查看活动详情
1. 对象类型
通过对象类型来描述对象,有三种方式可以描述对象
- 通过匿名的形式
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;
}
2. 属性修饰符
2.1 可选属性
可以在属性后面增加一个 ? 表示是可选的
interface PaintOptions {
xPos?: number;
yPos?: number;
}
function paintShape(opts: PaintOptions) {}
paintShape({ xPos: 100 });
paintShape({ yPos: 100 });
paintShape({ xPos: 100, yPos: 100 });
这种情况一般会结合解构语法提供属性默认值
function paintShape({ xPos = 0, yPos = 0 }: PaintOptions) {
console.log("x coordinate at", xPos); // (parameter) xPos: number
console.log("y coordinate at", yPos); // (parameter) yPos: number
}
2.2 readonly 属性
使用 readonly 属性后,属性本身是不能被修改的。但是,如果属性的值是对象,该属性内部的属性值是可以被修改的
interface Person {
readonly info: { name: string; age: number };
}
function normalHandle(person: Person) {
console.log(`Happy birthday ${person.info.name}!`);
person.info.age++;
}
function errorHandle(person: Person) {
person.info = { // error TS2540: Cannot assign to 'info' because it is a read-only property.
name: "leon",
age: 24,
};
}
检查两个类型是否兼容的时候不会考虑 readonly 属性,也就是说 readonly 的值可以通过别名进行修改。
interface Person {
name: string;
age: number;
}
interface ReadonlyPerson {
readonly name: string;
readonly age: number;
}
let writablePerson: Person = {
name: "Person McPersonface",
age: 42,
};
let readonlyPerson: ReadonlyPerson = writablePerson;
console.log(readonlyPerson.age); // prints '42'
writablePerson.age++;
console.log(readonlyPerson.age); // prints '43'
3. 属性继承和交叉类型
接口可以使用 extends 关键字从其他接口中继承类型,继承的类型会合并到当前类型。
interface Colorful {
color: string;
}
interface Circle {
radius: number;
}
interface ColorfulCircle extends Colorful, Circle {
id: number;
}
const cc: ColorfulCircle = {
id: 110,
color: "red",
radius: 42,
};
交叉类型使用 & 符号,也可以用于合并已经存在的对象类型
interface Colorful {
color: string;
}
interface Circle {
radius: number;
}
function draw(circle: Colorful & Circle) {
console.log(`Color was ${circle.color}`);
console.log(`Radius was ${circle.radius}`);
}
// okay
draw({ color: "blue", radius: 42 });
// oops
draw({ color: "red", raidus: 42 });
// error TS2345: Argument of type '{ color: string; raidus: number; }' is not assignable to parameter of type 'Colorful & Circle'.
// Object literal may only specify known properties, but 'raidus' does not exist in type 'Colorful & Circle'. Did you mean to write 'radius'?
属性继承和交叉类型最大的区别就是冲突的处理方式,属性继承遇到属性冲突的时候直接报错,而交叉类型不会报错。下面的例子中,通过交叉类型进行处理后,color 的类型是 never,取得是 string 和 number 的交集。
interface Colorful {
color: string;
}
interface ColorfulSub extends Colorful {
color: number;
}
// error TS2430: Interface 'ColorfulSub' incorrectly extends interface 'Colorful'.
// Types of property 'color' are incompatible.
// Type 'number' is not assignable to type 'string'.
interface Colorful {
color: string;
}
type ColorfulSub = Colorful & {
color: number
}
4. 泛型对象类型
通过使用泛型,可以生成一个类型模版,可以代替多个具体的类型。
interface Box<Type> {
contents: Type;
}
interface StringBox {
contents: string;
}
interface NumberBox {
contents: number;
}
let boxAA: Box<string> = { contents: "hello" }; // hello
console.log(boxAA.contents);
let boxAB: Box<number> = { contents: 11 }; // 11
console.log(boxAB.contents);
let boxBA: StringBox = { contents: "world" }; // world
console.log(boxBA.contents);
let boxBB: NumberBox = { contents: 22 }; // 22
console.log(boxBB.contents);
5. 小结
首先介绍了描述对象类型的三种方式,匿名、接口和类型别名,接着介绍了可选属性和 readonly 属性,再介绍了属性继承和交叉类型的共同点和区别,最后介绍了泛型对象类型。