Skip to content

接口

接口(Interfaces)来定义对象的类型。

  • 一般首字母大写
  • 赋值时,变量须和接口保持一致
js
interface Person {
  name: string;
  age: number;
}
let tom: Person = {
  name: 'Tom'
}

// index.ts(6,5): error TS2322: Type '{ name: string; }' is not assignable to type 'Person'.
//   Property 'age' is missing in type '{ name: string; }'

接口可以定义多次,并自动合并

tsx
interface Point {
  x: number
}
interface Point {
  y: number
}
const point: Point = { x: 1, y: 2 }

可选 | 只读属性

可选属性:属性选填

只读属性:只能在创建时修改值

tsx
interface Person {
  readonly name: string
  age?: number
}

ts 提供的ReadonlyArray<T> 类型,它将可变方法去掉,保证数组创建后不能再修改

tsx
let a: number[] = [1, 2, 3, 4]
let ro: ReadonlyArray<number> = a
ro[0] = 12 // error!
ro.push(5) // error!
ro.length = 100 // error!
a = ro // error!

任意属性

用索引签名来表示任意属性

  • 一个接口只能定义一个任意属性
tsx
interface Person {
  name: string
  age?: number
  [propName: string]: any
}

let tom: Person = {
  name: 'Tom',
  gender: 'male'
}

定义了任意属性,确定属性可选属性的类型都必须是它的类型的子集

tsx
interface Person {
  name: string
  age?: number // 这里真实的类型应该为:number | undefined
  [propName: string]: string | number | undefined // 这样声明才不报错
}

绕开属性检测

鸭式辨别法

鸭式辨型法:像鸭子一样走路并且嘎嘎叫的就叫鸭子,即具有鸭子特征的认为它就是鸭子。

通过制定规则来判定对象是否实现这个接口。

tsx
interface LabeledValue {
  label: string
}
function printLabel(labeledObj: LabeledValue) {
  console.log(labeledObj.label)
}
printLabel({ size: 10, label: 'Size 10 Object' }) // Error

报错原因:参数接收对象,相当于直接赋值,有严格定义,不能多参或少参。

解决方法:通过外部变量传参

tsx
interface LabeledValue {
  label: string
}
function printLabel(labeledObj: LabeledValue) {
  console.log(labeledObj.label)
}
let myObj = { size: 10, label: 'Size 10 Object' }
printLabel(myObj) // OK

分析:myObj 不会经过额外属性检查,但会被推断为 {size: number, label: string},此时相当于两种类型赋值,参照鸭式辨别法,两种类型都具有 label 属性,所以认定为相同。

类型断言

类型断言:手动指定类型,ts 不会进行额外检测

tsx
// * as Props
interface Props {
  name: string
  age: number
  money?: number
}

let p: Props = {
  name: '兔神',
  age: 25,
  money: -100000,
  girl: false
} as Props // OK

索引签名

通过索引签名定义任意属性接收值

tsx
// [key: string]: any;
interface Props {
  name: string
  age: number
  money?: number
  [key: string]: any
}

let p: Props = {
  name: '兔神',
  age: 25,
  money: -100000,
  girl: false
} // OK

接口与类型别名区别

接口:定义数据模型

别名:给类型取个新名字

大多数情况下等效

对象和函数

两者都可以用来描述对象或函数的类型,但是语法不同

tsx
interface Point {
  x: number
  y: number
}

interface SetPoint {
  (x: number, y: number): void
}
tsx
type Point = {
  x: number
  y: number
}

type SetPoint = (x: number, y: number) => void

其他类型

与接口不同,类型别名还能作用于其他类型(原始类型、联合类型、元组)

tsx
// primitive
type Name = string

// object
type PartialPointX = { x: number }
type PartialPointY = { y: number }

// union
type PartialPoint = PartialPointX | PartialPointY

// tuple
type Data = [number, string]

// dom
let div = document.createElement('div')
type B = typeof div

接口可以定义多次,自动合并为单个接口。类型别名不行

js
interface Point {
  x: number;
}
interface Point {
  y: number;
}
const point: Point = { x: 1, y: 2 }

扩展

interface:继承方式扩展(extends)

type:交叉类型方式扩展(&)

tsx
interface PointX {
  x: number
}

interface Point extends PointX {
  y: number
}
tsx
type PointX = {
  x: number
}

type Point = PointX & {
  y: number
}

interface 和 type 之间也可以互相扩展

接口扩展类型别名

tsx
type PointX = {
  x: number
}
interface Point extends PointX {
  y: number
}

类型别名扩展接口

tsx
interface PointX {
  x: number
}
type Point = PointX & {
  y: number
}