高级类型

1.&:交叉类型(取所有类型的并集)

必须同时满足这俩接口里面的成员属性

interface DogInterface{
    run():void;
}
interface CatInterface{
    jump():void;
}
const pet: DogInterface & CatInterface={
    run(){},
    jump(){}
}

2.联合类型:

声明的类型并不确定,可以为多个类型中的一个

let age:string|number = '10'; // 可以为字符串
let age1:string|number = 10; // 也可以为数字

自变量类型

有的时候我们不仅需要限定一个变量的类型,而且还要限定变量的取值,在某一个特定的范围内

 // 自变量的联合类型
 let age2: 10|20|30 = 10;

对象的联合类型 

在类型不确定的情况下,只能访问所有类型的公有成员,即:取所有类型成员的交集

interface DogInterface{ run():void;  }
interface CatInterface{ jump():void; }
class Dogg implements DogInterface{
    run(){}
    eat(){}
}
class Catt implements CatInterface{
    jump(){}
    eat(){}
}
enum Types{Dog,Cat}
function getAnimal(type:Types){
    // 此处animal的类型为  animal: Dogg | Catt
    const animal = Types.Dog === type?new Dogg():new Catt();
    animal.eat(); //此时只能访问eat方法,因为eat是Dogg | Catt都有的方法.
    // animal.run(); //Error 类型“Dogg | Catt”上不存在属性“run”。
    return animal;
}

 

可区分的联合类型:结合了联合类型和自变量类型的一种类型保护方法

一个类型如果是多个类型的联合类型,并且 每个类型之间有一个公共的属性,那么我们就可以凭借这个公共属性,创建不同得类型保护区块,看下面的例子

interface SquareInterface{
     types:'Square';
     width:number
 }
 interface CircularInterface{
    types:'circular';
    radius:number
}

type Shape = SquareInterface | CircularInterface; // 联合类型
function getArea(s:Shape){
    switch(s.types){ // 通过两个类型的通用属性type,来创建不同的类型保护区块
        case 'Square':
            return s.width * s.width; // 在这可以访问SquareInterface
        case 'circular':
            return s.radius * s.radius * 3.14; // 在这可以访问CircularInterface
    }
}

let ss: SquareInterface = {
    types: 'Square',
    width:100
}
getArea(ss); // 10000

上面的案例如果添加一种新的保护区块会怎么样呢


// 添加一个长方形
 interface RectangleInterface{
    types:'rectangle';
    width: number;
    height:number
}
// 修改联合类型
type Shape = SquareInterface | CircularInterface | RectangleInterface;

// 使用
getArea( {
    types: 'rectangle',
    width:100, height:100
});
//结果: undefined

很显然上面的getArea(...)的结果是undefined,那么要怎么得到错误提示呢,有两种方法
1.给方法确定返回值,如果getArea()返回undefined,则不能满足;

function getArea(s:Shape):number{
    ...
}

此时给Shape新增联合类型的时候,由于getArea没有对应的方法,所以number则会提示报错

2.添加never

function getArea(s:Shape){
    switch(s.types){
        ...
        default:
            // 检查s是不是never类型,如果s是never类型,则前面所有的分支全部被覆盖,这个分支就不会走到, 如果是不是never类型,则前面的分支就遗漏掉
            return ((e: never) => { throw new Error(e) })(s)
    }
}

此时再给getArea传入不满足要求的参数,则会抛出一个错误信息

3.索引类型

先看一个例子

const obj = { a:1,b:2,c:3 }
function getArr(obj:any,keys:string[]){
    return keys.map(key=>obj[key])
}
console.log(getArr(obj,['b','c'])); // [2,3]
console.log(getArr(obj,['r','s'])); // [undefined,undefined]

当keys里面传了obj里面并不存在的数据是得到undefined,此时并没有报错.那么ts要怎么对这种类型进行约束呢?此时就要用到索引类型

索引类型的操作符
keyof T 表示 类型T的所有公共属性的自变量的联合类型
T[K] 类型T的属性K代表的类型
T extends U 泛型变量可以通过继承某个类型或者某个属性

接下来我们利用索引类型来改造上面的代码,就不会出现

const obj = { a:1,b:2,c:3 }
function getArr<T,K extends keyof T>(obj:T,keys:K[]):T[K][]{
    return keys.map(key=>obj[key])
}

console.log(getArr(obj,['r','s'])); 
// 此时编辑器会报错。Type 'string' is not assignable to type '"a" | "b" | "c"'.

4.映射类型

可以从一个旧的类型生成一个新的类型,例如,我们有如下一个接口,是可以随意修改的。

interface Obj { a:number;b:string}
let obj1: Obj = {a:1,b:'d'}
obj1.a = 3; // OK

 然后,我们看下,怎么把上面这个接口变成只读或者可选呢的呢,如下代码

// 设置只读:模拟Readonly<T>
type ReadonlyNew<T> = {
    readonly [P in keyof T]:T[P] // 索引出T中的所有元素(相当于for循环),设置成readonly,然后原样返回
}

type ReadonlyObj = ReadonlyNew<Obj>; // 只读

let obj2: ReadonlyObj = {a:2,b:'c'};
obj2.a = 3; // ERROR Cannot assign to 'a' because it is a constant or a read-only property.

//设置为可选: 模拟Partial
type PartialNew<T> = {
    readonly [P in keyof T]?:T[P]
}
type PartialObj = PartialNew<Obj>; // 可选
let obj3: PartialObj = { a: 2 } //OK
let obj4: Obj = { a: 2 } //Error

此时眼尖的同学看出来了,ReadonlyNew/PartialNew就是和TS内置的Readonly/Partial方法。

此外再介绍一种抽取Object子集的方法Pick,模拟Pick

interface Obj1 { a:number;b:number;c:number;d:number;}

type PickNew<T,K extends keyof T> = { // T中满足K的被保留了下来
    [P in K]:T[P] // 从K中选取,然后从T中返回
} 

type PcikObj = PickNew<Obj1, 'a'|'d'>; // 此时PcikObj里面就只有a和d两项了

以上三种类型Readonly/Partial/Pick,官方有个称号,叫同态,意味着只能作用于object属性,而不会引入新的属性

那么非同态是什么样的呢,我们来看下Record

interface Obj1 { a: number; b: number; c: number; d: number;}

type RecordObj = Record<'x'|'y',Obj1>
// 此时可以看到RecordObj是这样的
RecordObj = {
    x: Obj1;
    y: Obj1;
}

5.条件类型

T extends U ? X : Y    这一段是什么意思呢?
解:如果类型T可以被赋值给类型U,那么结果就是X类型,否则就是Y类型

type TypeName<T> = 
    T extends string ? "string" :
    T extends number ? "number" :
    T extends boolean ? "boolean" :
    T extends Function ? "function" :
    T extends undefined ? "undefined" :
    "object"
    
let t1: TypeName<string> = 'string';
type T2 = TypeName<number>; // type T2 = "number"

(A|B) extends U ? X : Y 可拆解成 (A extends U ? X : Y )  | (B extends U ? X : Y ) 
type t3 = TypeName<string | number>; // type t3 = "string" | "number"
根据这个特性可以实现一些类型的过滤,举个例子

type Diff<U,K> = U extends K ? never : U;

type dif = Diff<"a" | "b" | "c", "a" | "e">; 
// 结果: type dif = "b" | "c";
// (A|B) extends U ? X : Y ​​​​​​​可拆解成 (A extends U ? X : Y ​​​​​​​)  | (B extends U ? X : Y ​​​​​​​) 

// 根据上面的特性将Diff<"a" | "b" | "c", "a" | "e">拆卸成如下:
type _dif1 = Diff<"a", "a" | "e">; // never
type _dif2 = Diff<"b", "a" | "e">; // b
type _dif3 = Diff<"c", "a" | "e">; // c

type _dif = _dif1 | _dif2 | _dif3; // type _dif = "b" | "c";结果同diff

Diff的扩展,// 过滤掉某些类型

type NotNull<T> = Diff<T, undefined | null>;
type ts = NotNull<string | number | null | undefined>; // string|number

这些方法官方同样的有内置方法,对应的为

  • Exclude<T, U> -- 从T中剔除可以赋值给U的类型。
  • Extract<T, U> -- 提取T中可以赋值给U的类型。
  • NonNullable<T> -- 从T中剔除nullundefined
  • ReturnType<T> -- 获取函数返回值类型。
  • InstanceType<T> -- 获取构造函数类型的实例类型。
type T00 = Exclude<"a" | "b" | "c" | "d", "a" | "c" | "f">;  // "b" | "d"
type T01 = Extract<"a" | "b" | "c" | "d", "a" | "c" | "f">;  // "a" | "c"

type T02 = Exclude<string | number | (() => void), Function>;  // string | number
type T03 = Extract<string | number | (() => void), Function>;  // () => void

type T04 = NonNullable<string | number | undefined>;  // string | number
type T05 = NonNullable<(() => string) | string[] | null | undefined>;  // (() => string) | string[]

function f1(s: string) {
    return { a: 1, b: s };
}

class C {
    x = 0;
    y = 0;
}

type T10 = ReturnType<() => string>;  // string
type T11 = ReturnType<(s: string) => void>;  // void
type T12 = ReturnType<(<T>() => T)>;  // {}
type T13 = ReturnType<(<T extends U, U extends number[]>() => T)>;  // number[]
type T14 = ReturnType<typeof f1>;  // { a: number, b: string }
type T15 = ReturnType<any>;  // any
type T16 = ReturnType<never>;  // any
type T17 = ReturnType<string>;  // Error
type T18 = ReturnType<Function>;  // Error

type T20 = InstanceType<typeof C>;  // C
type T21 = InstanceType<any>;  // any
type T22 = InstanceType<never>;  // any
type T23 = InstanceType<string>;  // Error
type T24 = InstanceType<Function>;  // Error

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值