02-TypeScript进阶

1.keyof 操作符

ts内置工具类型,
通过 keyof 操作符可以获取对象中的所有键类型组成的联合类型

demo1
获取对象中的所有键类型组成的联合类型

interface ICat {
    name: string,
    age: number
}
// keyof 后面是类型,而不是一个值
// 等价于: type keys = "name" | "age"
type keys = keyof ICat

let a1: keys = 'a' //报错
let a2: keys = 'name' //不报错
let a3: keys = 'age' //不报错

demo2
获取对象类型所有属性的类型

type Person = {
    id: number;
    name: string;
    isCh: boolean;
};

// 等价于 type P1 = 'id' | 'name' | 'age'
type P1 = keyof Person; 

// 等价于 type P2 = number | string | boolean
// 由 type P2 = Person['id'] | Person["name] | Person["isCh"] 推导而来
type P2 = Person[keyof Person];  

let p1: P2 = undefined  //报错
let p2: P2 = 1  //不报错
let p3: P2 = 'aa'  //不报错

demo3
下面代码报错

type Info = {
    name: string
    age: number
}

function getVal(obj: Info, key: any) {
    // 报错, 因为如果key是obj不存在的属性,则返回undefined
    // 应该约束入参key是obj存在的属性
    return obj[key] 
}

解决方案:

type Info = {
  name:string
  age:number
}

function getVal(obj:Info, key:keyof Info) {
  return obj[key]
}

demo4

type A = keyof any
// 等价于以下只能用于索引的类型
// type A = string | number | symbol

2.typeof 操作符

ts内置工具类型
TS中的typeof有新的用途,那就是获取一个变量的类型并且能够用它声明新的变量

demo1

let a = '123'
// 获取a的类型为string, 
//下面的代码等价于: type NewType = string;
type NewType = typeof a;
let b: NewType = 3 //报错,因为b定义为字符串类型

demo2

// 需求: 需要一个和cat结构相同的新类型
let cat = {
    name: 'ha',
    age: 16
}

// 方案一: 用interface手动定义
interface ICat1 {
    name: string,
    age: number
}


// 方案二: 用type手动定义
type ICat2 = {
    name: string,
    age: number
}

let c1: ICat2 = {
    name: 'aa',
    age: 45
}

//方案三: 用typeof基于已有对象cat实现
// 鼠标放在ICat3上可以查看类型
type ICat3 = typeof cat
let c2: ICat3 = {
    name: 'aa',
    age: 45
}

demo3

// Colors在定义时没有加as const

const Colors = {
    Red: '#FF0000',
    White: '#FFFFFF'
}
Colors.Red = 'aaa' // Red的值可以更改
// Colors = {} //报错,因为Colors的地址不能更改

// 等价于 type Color = { Red: string, White: string}
type Color = typeof Colors

//不报错
let a: Color = {
    Red: 'red',
    White: '000'
}

demo4

// Colors在定义时加as const, 帮助ts进行类型推导
const Colors = {
    Red: '#FF0000',
    White: '#FFFFFF'
} as const

// Colors.Red = 'aaa' // 报错,Red的值也是常数,不能修改
// Colors = {} //报错,因为Colors的地址不能更改

// 等价于 type Color = { readonly Red: '#FF0000', readonly White: '#FFFFFF'}
type Color = typeof Colors

//报错
let a1: Color = {
    Red: 'red',
    White: '000'
}
//不报错
let a2: Color = {
    Red: '#FF0000',
    White: '#FFFFFF'
}

3.extends关键字

extends关键字在TS编程中出现的频率挺高的,而且不同场景下代表的含义不一样,特此总结一下:

  • 继承/拓展
  • 泛型约束
  • 条件类型(三元表达式类型)

表示继承/拓展的含义

demo1
继承父类的方法和属性

  class Animal {
      //定义kind属性类型
      kind: string
      constructor(kind: string) {
          this.kind = kind;
      }
      sayHello() {
          console.log(`Hello, I am a ${this.kind}!`);
      }
  }

  class Dog extends Animal {
      constructor(kind: string) {
          super(kind)
      }
      bark() {
          console.log('wang wang')
      }
  }

  const dog = new Dog('dog');
  console.log(dog.kind); //  => 'dog'
  dog.sayHello(); // => Hello, I am a dog!

demo2:
继承某个类型

interface Animal {
    kind: string;
}

// Dog类型继承Animal类型的定义
interface Dog extends Animal {
    bark(): void;
}
// Dog => { kind: string; bark(): void }


let d1: Dog = {
    kind: 'animal',
    bark: 12  //报错,bark是方法
}

let d2: Dog = {
    bark(){},  //报错, 缺少kind属性
}

//不报错
let d3: Dog = {
    bark(){}, 
    kind: 'animal'
}

泛型约束

demo1

//表示入参和返回值都必须是P类型,而P必须是number、string、boolean中的一种
// 用来缩窄泛型的类型
function fun<P extends number | string | boolean>(param: P): P {
    return param;
}

fun('string'); // ok

fun(1); // ok

fun(true); // ok

fun(null); // ts(2345) 'null' 不能赋予类型 'number | string | boolean'

demo2

interface ICat1 {
    age: number
}
interface ICat2 {
    cname: string,
    age: number
}

// T是继承自{cname:string}的类型,也就是要包含cname定义
// 入参entities是T类型的数组
// 函数返回值是字符串数组
function getCnames<T extends { cname: string }>(entities: T[]): string[] {
    return entities.map(entity => entity.cname)
}

let list = [{
    cname: 'ha',
    age: 123
}]
// getCnames<ICat1>(list)  //报错,因为ICat1中没有cname属性

getCnames<ICat2>(list)  //不报错

getCnames(list)  //不报错,因为会自动推导T是{cname: string, age: number}

demo3

interface Store<State extends { id: number; name: string }> {
    state: State
}

// A1 即为 { state: 类型 }, 但类型不确定,泛型传入什么类型,state就应该定义为这种类型
type A1 = Store<{ id: number; name: string; }>; // 不报错

type A2 = Store<{ id: number; name: string; age: number; }>; // 不报错

type A3 = Store<{ id: string; name: number; }>; // 报错,因为name为string

type A4 = Store<{ id: number; }>; // 报错,因为少了name属性


//报错
let a1: A1 = {
    state: {
        id: '11',
        name: 'ha'
    }
}

//报错
let a2: A1 = {
    state: {
        id: 1,
        name: 'ha',
        age: 12
    }
}

//不报错
let a3: A1 = {
    state: {
        id: 1,
        name: 'ha',
    }
}

条件类型

type Human = {
    name: string;
}
type Duck = {
    name: string;
}
type Bool = Duck extends Human ? 'yes' : 'no'; // Bool => 'yes'

let a1: Bool = 1 //报错, 因为a1只能赋值'yes'

// 示例2
interface A1 {
  name: string
}

interface A2 {
  name: string
  age: number
}
// A的类型为string
type A = A2 extends A1 ? string : number

const a: A = 'this is string'
type A1 = 'x' extends 'x' ? string : number; // string

let a1 : A1 = 3  // 报错,因为a2是string类型
type A2 = 'x' | 'y' extends 'x' ? string : number; // number
let a2: A2 = '12' //报错
type P<T> = T extends 'x' ? string : number;

let a3: P<'x'> = 123  //报错,因为当T='x'时, T extends 'x'为真,则 P<'x'>即为string类型
let a4: P<'y'> = 'aa' //报错,应该赋值number类型
  • 8
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值