typeScript类型

68 阅读4分钟

下一章typeScript操作符

基本类型

元组 Tuple

类似数组,限制元素数量;

let x: [string, number];

枚举

类似对象,一般用于定义有映射关系的常量

enum StatusEnum { success = 0, danger = 1, warning = 2 }

枚举的便利是既可以通过名字获取枚举值,也可以通过枚举值获取名字

let successVal: StatusEnum = StatusEnum.success; // 0
let successName: string = StatusEnum[0]; // 'success'

联合类型

属性为多种类型之一

基础类型

类似js中的||

使用时,只能访问此联合类型的所有类型里共有的属性或方法

let a: string | number;
a.length // Error:类型“number”上不存在属性“length”

使用typeof作类型保护

let a: string | number;
if (typeof a === 'string') {
  // string
  a.length; // OK
} else {
  //number
}

在赋予变量的值之后,ts会自动推断所属类型,此后只可调用对应类型的方法

对象类型

赋值时,数据结构需满足二者之一的类型结构

interface Student {
  name: string;
  score: number;
}
interface Teacher {
  name: string;
  age: number;
}
type User = Teacher | Student
let user: User;
user = {name: 'zs', age: 123};//OK
user = {name: 'zs', score: 123};//ok

下面的情况是可以通过的

let user: User = {name: '张三', age: 18, score: 12};
//只能访问共同属性
user.name;//OK
user.age;//Error

访问属性时,只能访问联合中的共同属性

interface Student {
  name: string;
  score: number;
}
interface Teacher {
  name: string;
  age: number;
}
type User = Teacher | Student
let user: User;
user.name; // OK
user.score; // Error类型“Teacher”上不存在属性“score”
user.age; // Error类型“Student”上不存在属性“age”

如果需要访问的是非共同的属性,必须做好类型保护

使用in帮助ts识别类型

interface Student {
  name: string;
  score: number;
}
interface Teacher {
  name: string;
  age: string;
}
type User = Teacher | Student
function test(user: User) {
  if ('score' in user) { //Student
    console.log(user.score);
  }else{ //Teacher
    console.log(user.age)
  }
}

赋值后访问属性

const user: User = { name: 'zs', score: 123 }; // OK
user.name; //OK
user.score;//OK

TS类型兼容性

类型兼容性用于确定一个类型是否能赋值给其他类型。

其基本规则是,如果x要兼容y,那么y至少具有与x相同的属性。比如:

interface Named {
    name: string;
}
let x: Named;
// y's inferred type is { name: string; location: string; }
let y = { name: 'Alice', location: 'Seattle' };
x = y; //ok

交叉类型

用于组合多个类型为一个类型(常用于对象类型),新的类型包含所有类型的特性

接口类型的交叉

交叉后的类型包含所有接口内的属性

interface Student {
  name: string;
  age: number;
  score: number;
}
interface Teacher {
  name: string;
  age: number;
}
type User = Teacher & Student
//相当于type User = {name: string, age: number, score: number}
let user = {} as User;
user.name;//ok
user.score;//ok

联合类型的交叉

交叉后的类型需满足所有类型,相当于所有类型的交集。

type typeA = 'day' | 'week';
type typeB = 'day' | 'month';
type typeAB = typeA & typeB

那么typeAB的类型为

'day'

如果无法产生交集,则为never类型

type typeA = 'week';
type typeB = 'month';
type typeAB = typeA & typeB; // nerver

同时,在对对象类型进行交叉运算的时候,如果对象中相同的属性被认为是可辨识的属性(即属性的类型是字面量类型或字面量类型组成的联合类型),那么如果此类型不同且没有包含关系,则最终的运算结果将是 never 类型:

type typeA = {name: 'a', age: number};
type typeB = {name: 'b', age: number};
type typeAB = typeA & typeB; // never

函数类型的交叉

使用交叉类型可以将不同参数类型的函数进行合并

type FnA = (a: string) => void;
type FnB = (a: number) => void;
type FnAB = FnA & FnB;

那么FnAB就相当于

(a: string | number) => void

泛型

泛型用于创建可重用的组件,一个组件可以支持多种类型的数据。

一般用于定义一个不确定类型的函数/接口/类型,不预先指定具体的类型,而是在使用的时候在指定类型。 官方示例:

function identity<T>(arg: T): T {
    return arg;
}

定义后可使用两种方法指定类型,

第一种,使用<>明确指定类型

let output = identity<string>("myString");  // type of output will be 'string'

第二种,利用类型推论 – 即编译器会根据传入的参数自动地帮助我们确定T的类型:

let output = identity("myString");  // type of output will be 'string'

使用泛型变量时需注意,必须把这些泛型参数当做是任意或所有类型s

function loggingIdentity<T>(arg: T): T {
    console.log(arg.length);  // Error: T doesn't have .length。函数可能传入的是个数字,而数字是没有 .length属性的
    return arg;
}

对于上面的问题,可以通过泛型约束来解决

泛型约束

泛型约束即是对泛型的类型进行约束控制。

对于上面无法访问length属性的情况,我们可以创建一个包含length属性的接口,配合extends关键字实现约束。

interface LengthInterface {
    length: number;
}

function loggingIdentity<T>(arg: T extends LengthInterface): T {
    console.log(arg.length);// OK
    return arg;
}

经过约束之后,传入的值就必须符合约束类型

泛型默认类型

interface A<T = string> {
  name: T
}