陆陆续续从文档上手TypeScript
,发现仍然还是有很多不懂。
比如各种框架的常用类型,ts
中内置的常用类型,以及一些容易被忽略和遗忘的点,陆陆续续顺手把他们写到文章中记录起来。
TS
keyof
The
keyof
operator takes an object type and produces a string or numeric literal union of its keys
keyof
操作符会将一个对象类型(注意这里是类型并不是值)的key
组成联合类型返回。
interface IProps {
name: string;
count: number;
}
type Ikea = keyof IProps; // Ikea = 'name' | 'count'
function testKeyof(props: Ikea): void { }
复制代码
extends
Ts
中extends
除了用在继承上,还可以表达泛型约束,通过extends
关键字可以约束泛型具有某些属性。
其实
extends
关键字表示约束的时候,就是表示要求泛型上必须实现(包含)约束的属性。
Demo
比如
function loggingIdentity<T>(arg: T): T {
console.log(arg.length) // Ts语法错误 T可以是任意类型,并不存在length属性
return arg
}
复制代码
我们定义一个接口来描述约束条件,创建一个包含 .length
属性的接口,使用这个接口和 extends
关键字来实现约束:
interface Lengthwise {
length: number
}
// 表示传入的泛型T接受Lengthwise的约束
// T必须实现Lengthwise 换句话说 Lengthwise这个类型是完全可以赋给T
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length) // OK
return arg
}
复制代码
现在这个泛型函数被定义了约束,因此它不再是适用于任意类型:
loggingIdentity(3); // Error
复制代码
我们需要传入符合约束类型的值,必须包含必须的属性:
loggingIdentity({length: 10, value: 3}) // OK
复制代码
你可以声明一个类型参数,且它被另一个类型参数所约束。 比如,现在我们想要用属性名从对象里获取这个属性。 并且我们想要确保这个属性存在于对象 obj
上,因此我们需要在这两个类型之间使用约束。
function getProperty<T, K extends keyof T> (obj: T, key: K ) {
return obj[key]
}
let x = {a: 1, b: 2, c: 3, d: 4}
getProperty(x, 'a') // okay
getProperty(x, 'm') // error
复制代码
表示传入的两个参数,第二个参数被约束成为只能传入
obj
的key
类型。
ReturnType<Type>
Constructs a type consisting of the return type of function
Type
.
ResultType<type>
接受传入一个函数类型为泛型,返回值为函数的返回类型。
Demo
type T0 = ReturnType<() => string>; // type T0 = string
type T1 = ReturnType<(s: string) => void>; // type T1 = void
复制代码
查阅
ReturnType
源代码中的类型定义,发现使用了infer
类型定义。
infer
infer
表示在extends
条件语句中待推断的类型变量,必须联合extends
类型出现。
demo
type ParamType<T> = T extends (...args: infer P) => any ? P : T;
interface User {
name: string;
age: number;
}
type Func = (user: User) => void;
type Param = ParamType<Func>; // Param = User
type AA = ParamType<string>; // string
复制代码
其实碰到infer
关键字简单的将它理解为一个等待推断的类型(我现在不知道,得根据条件(extends
)来判断)就可以了。
重点是:
infer
跟随extends
成双出现。infer P
表示类型P
是一个待推断的类型。(不使用infer
直接P
会报错)Record<Keys,Type>
构造一个对象类型,其属性键为Keys
,属性值为Type
。此实用程序可用于将一种类型的属性映射到另一种类型。
type keys = 'name' | 'title' | 'hello';
interface values {
name: string;
label?: number;
}
// Record内置类型可以将 传入的keys联合类型遍历作为key
// 为每一个key的value赋值为 values从而形成一个全新的对象类型返回
const b: Record<keys, values> = {
name: {
name: 'wang',
label: 1,
},
title: {
name: 'hellp',
},
hello: {
name: 'nihao',
},
};
复制代码
看看它的源码本质上很简单,就是遍历传入的范型T
作为key
,将传入的范型value
作为值的类型。
Pick<Type, Keys>
Pick的定义很简单,就是从传入的Type中跳出对应的Keys属性,从而返回新的类型。
interface Props {
name: string;
label: number;
value: boolean;
}
type ChildrenProps = Pick<Props, 'name' | 'label'>;
复制代码
Parameters<T>
Parameters<T>
用于获得函数的参数类型组成的元组类型
源码中这样定义的
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any
? P : never;
复制代码
Exclude<T,U>
Exclude
是进行排除联合类型中的一部分内容,相对于下面的Omit
操作符来说Omit
是针对于key&value/接口
形式的,而Exclude
是针对于联合类型
来操作的。
let a: string | number;
type CustomType = Exclude<typeof a, string>; // number类型
复制代码
Exclude
的原理
type Exclude<T, U> = T extends U ? never : T
传入两个泛型
我们这里用 typeof a
也就是 string | number
去代表 T
用 string
属性去代表第二个泛型 U
T extends U
就判断是否string | number
有 string
, 有string
就返回never
,就代表将其排除
Omit<T, K>
3.5 版本之后,TypeScript 在 lib.es5.d.ts 里添加了一个
Omit<T, K>
帮助类型。Omit<T, K>
类型让我们可以从另一个对象类型中剔除某些属性,并创建一个新的对象类型。
比如:
`type User = {`
`id: string;`
`name: string;`
`email: string;`
`};`
`type UserWithoutEmail = Omit<User, "email">;`
`// 等价于:`
`type UserWithoutEmail = {`
`id: string;`
`name: string;`
`};`
复制代码
new
关键字ts
中new()
表示构造函数类型。
当我们声明一个类的时候,其实声明的是这个类的实例类型和静态类型两个类型。
比如
// type: { new(): T }
// 表示函数接受的参数type,是一个对象,这个对象有一个构造函数返回T
// 换句话说type是一个类类型
function factory<T>(type: { new (): T }): T {
return new type();
}
class Person {
static myName; // 类的静态属性
public yourName; // 类的实例属性
}
// 传入的范型Person指类的实例类型
const person = factory<Person>(Person);
复制代码
typeof 类
ts
中通过typeof 类
可以获得类的类类型,直接使用类作为类型此时使用的是类的实例类型。
let a: Person; // Person表示类的实例类型
a.yourName;
let b: typeof Person; // typeof Person 表示类的类类型
b.myName;
let c: { new (): Person } = Person; // c为构造函数类型,c拥有一个构造函数,也就是new c() 返回的是Person的实例。表示c是Person类。
复制代码
React & TS
内置类型React.ReactNode
源码类型中关于ReactNode
的类型定义
type ReactNode = ReactChild | ReactFragment | ReactPortal | boolean | null | undefined;
复制代码
可以看到ReactNode
是一个类型别名,他是多种类型组成的联合类型。
其中ReactChild
为type ReactChild = ReactElement | ReactText;
ReactPortal
定义
interface ReactPortal extends ReactElement {
key: Key | null;
children: ReactNode;
}
复制代码
所以
ReactNode
是包含ReactElement
类型的联合类型。换句话说ReactElement
可以赋给ReactNode
,但反过来不可以。
React.element
interface ReactElement<P = any, T extends string | JSXElementConstructor<any> = string | JSXElementConstructor<any>> {
type: T;
props: P;
key: Key | null;
}
复制代码
可以看到React.Element
类型接受传入两个泛型分别是jsx
编译后的vdom
对象的props
和组件本身
。
返回的仅仅是包含type,props,key
的一个Object
对象。