ts高级类型

2020-04-17  本文已影响0人  sweetBoy_9126

类型的且运算

interface A {
  name: string,
  age: number
}
interface B {
  name: string,
  garde: number
}
const c: A & B = {
  name: 'lifa',
  age: 18,
  garde: 100
}

上面的代码c变量的类型为A和B,也就是要同时满足A和B里所有的字段定义,name、age和garde少一个都会报错

在react中使用且运算
场景:我们声明一个layout函数组件

const Layout: React.FunctionComponent = () => {
    return (
        React.createElement('div', null, 'hi')
    )
}

然后我们需要给Layout加一个Header属性,但是我们的Layout上并没有Header属性,如果直接声明会报错

const Layout: React.FunctionComponent & { Header: React.FunctionComponent} = () => {
    return (
        React.createElement('div', null, 'hi')
    )
}
Layout.Header = () => {
    return (
        React.createElement('div', null, 'hi')
    )
}

方法2:使用继承

interface Layout2 extends React.FunctionComponent {
    Header: React.FunctionComponent
}
const Layout2: Layout2 = () => {
    return (
        React.createElement('div', null, 'hi')
    )
}
Layout2.Header = () => {
    return (
        React.createElement('div', null, 'hi')
    )
}

从上面的代码可以看出来ts可以声明接口名和变量名是同一个名字,它会自动给你匹配

类型的或运算

interface A {
    name: string,
    age: number
}
interface B {
    name: string,
    grade: number
}
const c: A | B = {
    name: 'lifa',
    age: 18
}

function add(a: string | number, b: string | number) {
    return a + b
}

类型别名type

对于上面的layout我们指定它的类型为:React.FunctionComponent & { Header: React.FunctionComponent},这样看起来代码非常长,我们可以将它们单独拿出来声明一个type,赋值给type

type Layout = React.FunctionComponent & { Header: React.FunctionComponent}

type和interface的区别

type是给一个已知的类型取了一个别名,而interface是声明了一个新的类型,在我们不知道该用type还是interface的时候最好用interface

// 将已知的string类型命名为Name
type Name = string
const name1: Name = '小小四'

字面量类型

所谓的字面量类型就是不直接声明类型,而使用一个值来代替这个类型,比如1就可以代替number,'1'就可以代替string,ts会自动根据你的值匹配对应的类型

interface Women {
        name: 'boduo' | 'cangjing' | 'zhaoliying'
    }
    const wife: Women = {
        name: 'zhaoliying'
    }

上面我们直接通过字面量来指定string类型,wife的name值只能是三个中的一个

更多例子:

type Dir = 'east' | 'west' | 'north' | 'south'
const dir: Dir = 'east'
type B = true | false | 6 | 'west'
const c: B = true

interface Course {
  name?: string,
  // 等价于
  name1: string | undefined
}

this类型

  1. 没有继承关系的类
    当我们直接对一个基础的类实例化的时候,它里面的this会指向这个类,所以当返回this的时候,还可以继续调用这个类的方法。
class Calc {
    public value: number
    constructor(n: number) {
        this.value = n
    }
    add(n: number) {
        this.value += n
        return this
    }
    multiple(n: number) {
        this.value *= n
        return this
    }
}
const c = new Calc(1)
c.add(2).multiple(3)
  1. 有继承关系的类
class Calc {
    public value: number
    constructor(n: number) {
        this.value = n
    }
    add(n: number) {
        this.value += n
        return this
    }
    multiple(n: number) {
        this.value *= n
        return this
    }
}
class BiggerCalc extends Calc {
    sin() {
        this.value = Math.sin(this.value)
        return this
    }
}
const c = new BiggerCalc(1)
c.add(2).multiple(3).sin()

当我们以子类生成一个实例对象的时候,this就会指向我们的子类,它既可以调用父类的方法,也可以调用自己的方法

总结:

this既可以指向你的父类又可以指向你的子类,具有多种状态所以this是多态的

索引类型

  1. 在我们不确定我们的参数的个数的情况下,我们可以直接通过指定属性的key为string,然后属性的value为任意类型,这样我们就可以在后期在原有的接口属性的基础上添加或修改了
    比如:
const calender = (options: CalenderOptions) => {

}
interface CalenderOptions {
    [k: string]: any
}
calender({
    time: Date.now,
    view: 'year'
})

我们的CalenderOptions[k: string]: any,这样我们的calender在调用的时候里面的属性就可以随意的添加了

  1. 检索属性的key是否是对应对象里的key
function pluck<T, K extends keyof T>(object: T, kes: Array<K>) {

}
pluck({ name: 'lifa', age: 18, habit: '女' }, ['name', 'age'])

代码解释

T: { name: string, age: number, habit: string }
keyof T: 'name' | 'age' | 'habit' (也就是T的键名)
因为K集成keyof T所以 K也是'name' | 'age' | 'habit'

所以pluck的第二个参数必须是第一个参数里面的键名,如果不是就会报错

  1. 复杂的返回值类型
function pluck<T, K extends keyof T>(object: T, keys: Array<K>): T[K][] {
    return keys.map(key => object[key])
}
pluck({ name: 'lifa', age: 18 }, ['name', 'age'])

上面的T[K][]直接看的话我们很难理解,我们可以逆推,当我们调用pluck的时候拿到的返回值是['lifa', 18]

(1). ['lifa', 18] 换成类型就是 Array<string | number>
(2). string也就是T[name]number也就是T[age],就会变成Array<T[name] | T[age]>(因为上面的T就是{name: string, age: number})
(3). nameage也就是我们的K,所以可以改写成Array<T[K] | T[K]>,前后都是T[K]就可以写成一个Array<T[K]>
(4). Array<T[K]> 等价于 T[K][]

Readonly和Partial

interface Person {
    name: string,
    age: number
}
interface ReadonlyPerson {
    readonly name: string,
    readonly age: number
}

但这样写起来代码明显很冗余,我们还可以这样写

interface Person {
    name: string,
    age: number
}
type ReadonlyPerson2 = Readonly<Person>

我们来看一下Readonly的源码

type Readonly<T> = {
    readonly [P in keyof T]: T[P];
};

这里的in和前面的extends的区别是in是包含了数组里的每一项,也就是name和age都必须包含,而extends可以是其中的一项或多项,
type Readonly<{name: string, age: number}> = {
// 类型转换过程
(1). readonly name: T[name];
readonly age: T[age]
(2). readonly name: string;
readonly age: number
};

interface Person2 {
    name?: string,
    age?: number
}
// 等价于
type Person3 = Partial<Person>

可辨识联合

  1. 使用场景:
interface Props {
 acton: 'create'|'update';
 id?: number;
 name: string;
}

我们有一个借口Props里面有三个属性,其中action是联合的可以取create或者update,但是id是可选的,如果是create是没有id的,只有update的情况下才有id,但是很明显上面的接口不满足我们的需求,所以我们需要写成下面这样

type Props = {
    action: 'create',
    name: string
} | {
    action: 'update',
    name: string,
    id: number
}

声明使用:

const p1: Props = {
    action: 'create'
}
const p2: Props = {
    action: 'update'
}
const p3: Props = {
    action: 'create',
    name: 'TS入门'
}
const p4: Props = {
    action: 'update',
    name: 'TS入门'
}
const p5: Props = {
    action: 'update',
    name: 'Ts入门',
    id: 1
}

上面只有p3p5是满足条件的;
问题:如果我们声明一个接口满足Props类型,直接打印出它的id会报错;
原因:Props里不一定有id属性

解决方法:通过他们共有的一个action属性来判断id,如果action等于update就有id,否则没有

function fn(a: Props) {
     if (a.action === 'update') {
         console.log(a.id)
     } else {
         console.log(a.name)
     }
}
  1. 可辨识类型的前提
    (1). 有一个共有的字段,比如上面的action
    (2). 共有字段是可穷举的(值是有限制的,也就是说有固定的几个值;而不是无穷个,比如action: string,这就有无穷个值了,就不符合)
  2. Redux的Action中使用可辨识类型
type Action2 = {
    type: 'ADD',
    payload: number
} | {
    type: 'ADD_STRING',
    payload: string
} | {
    type: 'ADD_DATE',
    payload: Date
}
function reducer(state: any, action: Action2) {
    switch(action.type) {
        case 'ADD':
            action.payload = 1
            break
        case 'ADD_STRING':
            action.payload = '1'
            break    
    }
}
上一篇 下一篇

猜你喜欢

热点阅读