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类型