Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

typescript手册-对象的类型-接口 #16

Open
Pasoul opened this issue Mar 13, 2019 · 3 comments
Open

typescript手册-对象的类型-接口 #16

Pasoul opened this issue Mar 13, 2019 · 3 comments

Comments

@Pasoul
Copy link
Owner

Pasoul commented Mar 13, 2019

typescript中,我们使用接口(interface)描述对象的类型

什么是接口

在面向对象语言中,接口(interface)是很重要的概念,它是对行为的抽象,而具体如何行动由类(class)去实现(implement)

typescript中,接口不仅可以对类的行为进行抽象,也可以对对象的形状进行描述

用接口描述对象

我们通过一个简单的例子,来看接口如何对对象的形状进行描述的:

interface Person {
  name:string;
  age:number
}

let student:Person = {
  name: 'zhangsan',
  age: 18
};

上面的例子中,我们定义了接口Person,然后定义了变量student,变量的类型是Person,这样我们就约束了student的形状必须跟Person一致。即必须有nameage两个属性。

定义的对象,多一些属性是不被允许的:

interface Person {
  name:string;
  age:number
}

let student:Person = {
  name: 'zhangsan',
  age: 18,
  address: 'hangzhou'
};

我们为student对象新增了一个属性address,编译报错:

Type '{ name: string; age: number; address: string; }' is not assignable to type 'Person'.
  Object literal may only specify known properties, and 'address' does not exist in type 'Person'

意思是变量不能指定Person类型,因为变量只能指定在Person中的已知属性,而address不存在于类型Person

这里我们注意到{ name: string; age: number; address: string; }typescriptstudent做的类型推导,关于类型推导,我们后面再聊。

定义的对象,少一些属性也是不被允许的:

interface Person {
  name:string;
  age:number
}

let student:Person = {
  name: 'zhangsan'
};
Type '{ name: string; }' is not assignable to type 'Person'.
  Property 'age' is missing in type '{ name: string; }'

可见,接口Person要求对象一定具备nameage两个属性,即形状必须保持一致。

@Pasoul
Copy link
Owner Author

Pasoul commented Mar 13, 2019

对象的可选属性

有事我们希望对象的形状不完全匹配,比如我们定义了一个baby对象,这个对象一开始没有起名字:

interface Person {
  name?:string;
  age:number
}

let baby:Person = {
  age: 0
};

过了一段时间,baby的名字起好了:

interface Person {
  name?:string;
  age:number
}

let baby:Person = {
  name: 'tom',
  age: 1
};

此时name就是可选属性,可选属性意味着可在可不在。

即便我们定义了可选属性name,但是依然不允许我们新增额外的属性:

interface Person {
  name?:string;
  age:number
}

let baby:Person = {
  name: 'tom',
  age: 1,
  favorite: 'ball'
};
Type '{ name: string; age: number; favorite: string; }' is not assignable to type 'Person'.
  Object literal may only specify known properties, and 'favorite' does not exist in type 'Person'

@Pasoul
Copy link
Owner Author

Pasoul commented Mar 13, 2019

对象的任意属性

有时我们不希望接口限制对象的形状,比如我们定义了接口Person,它类似个人信息调查表,要求每个人(对象)的姓名是必填的,但是随着每个人的发展,有的慢慢的学会了英语(speakEnglish),有的进了大学(school),每个人(对象)都有不同的特点(属性):

interface Person {
  name: string;
  age?: number;
  [propName: string]: any;
}

let student:Person = {
  name: "zhangsan",
  speakEnglish: true,
  school: "清华大学"
}

使用[propName: string]: any;表示不限制对象的属性值类型,如果我们将其改成string

interface Person {
  name: string;
  age?: number;
  [propName: string]: string;
}

let student:Person = {
  name: "zhangsan",
  speakEnglish: true,
  school: "清华大学"
}

我们将student的类型指定为Person将会编译报错:
image

在指定 [propName: string]: string;的前置条件下,我们来看这些问题:

先看这一句报错,这个比较好理解,新增的属性需要满足属性值为string类型
Type 'true' is not assignable to type 'string'

下面这两个算是同类问题,一旦定义了任意属性,那么可选属性和确定属性的类型必须是它的类型的子集,很明显number不是string的子集
Property 'heart' of type 'number' is not assignable to string index type 'string'
Property 'age' of type 'number | undefined' is not assignable to string index type 'string'.

解决方法也很简单,将任意属性的类型改成联合类型或者任意类型any即可:
注意age是可选属性,属性值可能为undefined,因此typescript推导其类型为number|undefined,因此联合声明应该包含undefined
i```
nterface Person {
name: string;
heart: number;
age?: number;
[propName: string]: string|number|boolean|undefined;
}

let student:Person = {
name: "zhangsan",
heart: 1,
speakEnglish: true,
school: "清华大学"
}

@Pasoul
Copy link
Owner Author

Pasoul commented Mar 13, 2019

对象的只读属性

我们希望对象的一些属性在创建的时候就被赋值,之后就不能对其修改,这时我们可以使用readonly来定义只读属性。

我们创建了个人信息调查表,要求每个人的调查表都有id字段用于区分,并且该字段不能随意改动:

interface Person {
  readonly id: number;
  name: string;
  age: number
}

let student:Person = {
  id: 1,
  name: 'zhangsan',
  age: 18,
};

如果我们尝试修改id的值,编译将会报错:不能给id赋值因为它是一个常量或只读属性

student.id = 2;
Cannot assign to 'id' because it is a constant or a read-only property

我们可以思考一下:readonly只是在重新给只读属性赋值的时候才起作用吗?

如果我们在登记某个人的信息表的时候,忘记给他分配唯一标识id:

interface Person {
  readonly id: number;
  name: string;
  age: number
}

let student:Person = {
  name: 'zhangsan',
  age: 18,
};

编译将会报错:

Type '{ name: string; age: number; }' is not assignable to type 'Person'.
  Property 'id' is missing in type '{ name: string; age: number; }'.

总结一下就是readonly会在给对象赋值的时候就开始检查,该只读属性是否存在,并且不能修改属性值。

参考:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant