6.29 drilling notes - typescript

65 阅读5分钟

整理一下思路,

此前工作中,多是用typescript规定一下后端返回的数据类型,一些函数的返回值、参数类型。

今天研究的几个问题:

type和interface的区别?

type关键字只是一个类型别名。和interface不一样。

文档。interface可以重名再写一块来扩展之前定义的同名interface,type只能写一个,不能再打开、拓展了

interface SomeObj{    
   name1:string
}
interface SomeObj{     
name2:number //接口SomeObj会被拓展name2属性,是一个merge的过程,如果property重名则会报错
}
let obj:SomeObj={name1:'hello',name2:2}
console.log(obj)

9.5补充: interface不能: interface A = B|C 它只能声明一个单一的对象结构, type A = B|C 可以的 必须使用type定义类型的联合 interface用extends实现type的&(交叉)

typescript高级类型?

  1. intersection交叉类型   用&链接要并的类型    

注意是取并集,产生新的类型,注意要同时满足交叉的各个类型的要求才可以编译通过;

下面例子里student 如果少了个nameb就会报类型错,说b里有nameb还未出现在对象里

type a= {    
    name:string    
    age:number
}
type b={    
    nameb:string    
    age:number
}
let student:a&b={
    name:'hao',
    age:3,
    nameb:'wang'
}

和extends有啥区别: 文档

interface PersonA{    
     name:string //这里name是字符串
}
interface PersonB{    
     name:number//这里name是number
}
let student: PersonA&PersonB = {name:1}
//typescript对于同名的属性intersection的时候,
//如果该属性类型在各个子类型中不相同,则认为是never
//Type 'number' is not assignable to type 'never'.ts(2322)// The expected type comes from property 'name' 
// which is declared here on type 'PersonA & PersonB'//(property) name: never
//但如果有重复的类型,则会使用交集类型,比如PersonA的name 用string|number那么交叉集就会name:number

//同名属性extends的时候呢?
  1. union. | 联合。 这个变量可能是联合的类型的任选其一。但会发现如果定义的变量满足intersection其实也能编译通过,这是因为交叉类型作为并集,可以赋值到联合类型的各个类型上 所以不会报错. 可辨识discriminated 联合 每个接口具有标识自己的属性和对应的值。interface Rectangle {kind:'rect'}, interface Circle {kind:'circle'}
type a= {    
    name:string    
    age:number
}
type b={    
    nameb:string    
    age:number
}
let student:a|b={name:'hao',age:3,nameb:'wang'}//编译通过

怎么判断具体是那种类型?

 type a= {
    name:string
    age:number
    gendera:string
}

type b={
    nameb:string
    age:number
    genderb:string
}

let student:a|b={name:'hao',age:3,gendera:'male'}

function isB(man:a|b): man is b{
     return (<b>man).genderb!==undefined
}

 console.log(isB(student))//false

类型谓词 函数参数 is 某个类型

有什么用?

如果想在函数里调用student的gendera或genderb(调用仅属于一个字类型的成员时。会报错

image.png

所以需要先判断一下,判断类型的if就是 type guard

let student:a|b={name:'hao',age:3,gendera:'male'}

function isB(man:a|b): man is b{
    return (<b>man).genderb!==undefined
}
//Any time `isFish` is called with some variable, 
// TypeScript will *narrow* that variable to that specific type if the original type is compatible.

function getManGender(man:a|b){
    if(isB(man)){
        return man.genderb
    }
    return man.gendera
}

console.log(getManGender(student))

缩小联合类型的范围的过程叫narrowing,有instanceof、typeof、as等等不同的方法 看文档narrowing

  1. type alias

  2. number/string literal.     值只能为指定的字符串。经常见的: interface MyObj{   a:  'hello'|'no hello'}

  3. 索引类型 ----- 鼠掉了,项目用这个感觉真的要鼠掉了

type Person = { age: number; name: string; alive: boolean };

type Age = Person["age"];
//这个type就是number Person是个type,它可以用key来选,得到的是该key对应的type;
//注意,你不能const key="age" 然后Person[key],但你能type key = "age" Person[key]
type I2 = Person[keyof Person]; //这个type是Person['age'|'name'|'alive'],也就是number|string|boolean

用一下

type Person = {age:number; name:string; alive:boolean}

type Age = Person['age']

let my:Age ='a'//Type 'string' is not assignable to type 'number'.ts(2322)
let myf:Age = 0
type Person = {age:number; name:string; alive:boolean}

type MagicType = Person[keyof Person]


let myvar:MagicType = 0
let myvar2:MagicType = false
let myvar3:MagicType = 'a'
let myvar4:MagicType = null //Type 'null' is not assignable to type 'MagicType'.ts(2322)

看个复杂的

function pluck<T, K extends keyof T>(o:T,names:K[]):T[K][]{
    return names.map(n=>o[n])
}

拆解一下。
函数pluck,

    1. 接受的参数类型为<T, K extends keyof T>,
    • 1.1. K extends keyof T 泛型K的类型约束是keyof T,
    • 1.2. 假如T是type Person = {age:number; name:string; alive:boolean}那么K就是"age"|"name"|"alive",那么T[K]就是T["age"|"name"|"alive"],也就是 string|boolean|number
    1. 参数为 o:T,names:K[]
    1. 返回值类型为T[k] 类型构成的数组;结合1.2的例子,其实就是string[]或boolean[]或number[]
function pluck<T, K extends keyof T>(o:T,names:K[]):T[K][]{
    return names.map(n=>o[n])
}

type Person ={name:string, age:number, alive:boolean}

let aman = {name:'tom',age:2,alive:true}

console.log(pluck(aman,["name","age"]))//输出['tom',2]

可以看出这个函数的功能就是给定一个字段列表返回T这个类型的对象实例对应字段的值构成的数组。

  1. this多态类型 这个tsc编译后的代码很有意思。。但不是今天的主题
class BasicCalculator {
    public constructor(protected value: number = 0) { }
    public currentValue(): number {
        return this.value;
    }
    public add(operand: number): this {
        this.value += operand;
        return this;  // return this来向管道一样继续调用下去
    }
    public multiply(operand: number): this {
        this.value *= operand;
        return this;
    }
    // ... other operations go here ...
}

class ScientificCalculator extends BasicCalculator {
    public constructor(value = 0) {
        super(value);
    }
    public sin() {
        this.value = Math.sin(this.value);
        return this;
    }
    // ... other operations go here ...
}

let vv= new ScientificCalculator(2)
        .multiply(5) //返回this ,继续调用
        .sin()//返回this ,继续调用
        .add(1)//返回this ,继续调用
        .currentValue();

console.log(vv)

还有其他很多。Partial, Pick 。。。。

infer

有这样一个需求,给你一个Promise<不知道啥类型> ,你来定义一个PromiseType,拿出来 不知道啥类型

//define a PromiseType
type PromiseType<T>= ???

type ptype = PromiseType<Promise<string>>
type PromiseType<T> = T extends Promise<infer K>?K:T

传进去泛型T,T是不是promise?不是,那么就返回T。是Promise?那么就要用infer来推断一下Promise<这个里面的类型>,把推断出的结果设为K,因为满足extends,则K 编译通过:

type PromiseType<T> = T extends Promise<infer K>?K:T

type MyPromiseReturnType = PromiseType<Promise<string>>

const a:MyPromiseReturnType = 'a'

type MyPromiseReturnType2 = PromiseType<Promise<number>>

const b:MyPromiseReturnType2 = 1

再来一个,定义一个FirstArg类型,能够拿出来一个函数类型的第一个参数的类型

type FirstArg<T> = T extends (first: infer F, ...args:any[])=>any?F:T;

type FA = FirstArg<(name:string,age:number)=>void>

以此类推,拿出函数返回值的类型:

type PersonHere = {
    person:string
}

type MyReturnType<T> = T extends (name:string,age:number)=>infer F?F:T;

type ReturnedType = MyReturnType<(name:string,age:number)=>PersonHere>//注意返回值就直接写个interface名就行了 ,这里只是描述一下类型

const mannn:ReturnedType = {person:'aaa'}

再来一个类型体操。。。缺氧了要

//type ArrayTypeList =?
type ArrayTypeList<T> = T extends (infer I)[]?I: T

type ItemType1 = ArrayTypeList<[string,number]>

const item1:ItemType1='a' // string|number

(infer I)[]