泛型
在 TypeScript 中,当我们想要描述两个值之间的对应关系时,会使用泛型。
泛型就像一条管道,将参数的类型流入到参数体内
函数泛型参数
虽然知道返回值是string类型,但是还是可以调用parseInt方法,没有报错。
使用泛型后,类型可以深入到函数内部
//案例将类型推断为string类型,只适用于基础数据类型
function echo<T>(arg: T): T {
return arg;
}
let test = echo("str");
test.parInt();
console.log(test);
函数多个泛型参数
//返回值自动被定义了对应类型
function swap<T, U>(tuple: [T, U]): [U, T] {
return [tuple[1], tuple[0]];
}
const result = swap(["string", 123]);
promise的案例
function withAPI(url: string) {
return fetch(url).then((resp) => resp.json());
}
withAPI("github.user").then((resp) => {});
interface GithubResp {
name: string;
count: number;
}
function withAPI<T>(url: string): Promise<T> {
return fetch(url).then((resp) => resp.json());
}
// 使用接口GithubResp定义了resp的类型
withAPI<GithubResp>("github.user").then((resp) => {});
react的案例
import * as React from "react";
const Test: React.FunctionComponent = (props) => {
console.log(props);
return (
<div>
{props.name}
{props.desc}
</div>
);
};
Test.displayName = "TestDis";
export default Test;
普通函数组件如果不添加类型,则props为any,也不会有props属性提示:
给普通函数组件添加React.FunctionComponent类型定义
- 会报错
给FunctionComponent添加泛型<Props>之后,props和Test类上的静态属性上都有了name和desc属性,可以自动补全属性了
- 上面报错是有因为
React.FunctionComponent是一个泛型接口,props默认类型定义为{}
涉及知识点1:接口泛型
案例
// react源代码中的定义
interface FunctionComponent<P = {}> { //泛型在接口上默认值为{},所以不传泛型也可以走下去
(props: PropsWithChildren<P>, context?: any): ReactElement<any, any> | null; //props: P 将泛型接口接口<Props>传递给了props,并基于<Props>进行转换
propTypes?: WeakValidationMap<P>; //propTypes: P 将接口传递给了propTypes,并基于<Props>进行转换
contextTypes?: ValidationMap<any>;
defaultProps?: Partial<P>; // defaultProps: P 将接口传递给了defaultProps,并基于<Props>进行转换
displayName?: string;
}
从案例中学到:
- 泛型接口可以定义默认值
interface FunctionComponent<P = {}> { } - 函数泛型接口可以将函数本身类型定义在接口内部
- FunctionComponent为函数的接口,但是接口内部也定义了函数本身的类型
(props: PropsWithChildren<P>, context?: any): ReactElement<any, any> | null;
- 泛型
P可以传递给属性,并可以基于该泛型P进行转换- 这就是props除了自定义的name和desc还有children属性?
//源码 interface FunctionComponent<P = {}> { (props: PropsWithChildren<P>, context?: any): ReactElement<any, any> | null; } type PropsWithChildren<P> = P & { children?: ReactNode }; //type 类型别名, &为合并类型,将P和类型{ children?: ReactNode }进行合并,所以看到props有三个属性name,desc,children - 这就是props除了自定义的name和desc还有children属性?
涉及知识点:
泛型约束 extends
function echo<T>(args:T):T{
console.log(args.length); //报错,因为并不知道T是什么类型,T上没有length属性
return args
}
改写
interface LengthType {
length: number; //只要泛型T有length属性就可以使用。 duck typing
}
function echo<T extends LengthType>(args:T):T{
console.log(args.length); //报错,因为并不知道T是什么类型,T上没有length属性
return args
}
echo('123');
echo({length: 123});
ts条件类型
type NoneType<T> = T extends undefined | null ? void : never;
let d1: NoneType<number>; //d1: never
let d2: NoneType<string>; //d2: never
let d3: NoneType<undefined>; //d3: void
keyof Type
keyof关键词后面为一个接口类型,拿到类型User的所有属性- 注意返回类型为
"id" | "name联合类型 - 一般结合
in关键词使用
type User = { id: string; name: string; };
type UserKeys = keyof User; //"id" | "name"
in
- 单独使用
keyof Type,只能拿到一个常量类型"id" | "name" - 结合
in使用,一行代码,可以搞定多个类型,并对类型进行转化,类型js中的循环
interface User {
name: string;
desc: string;
}
// 定义类型,将User中必选的类型都转化为可选
type Users = {
[key in UserKey]?: string;
};
let users: Users = {
}
带着知识点继续看React.FunctionComponent关于propTypes属性的定义
interface FunctionComponent<P = {}> {
propTypes?: WeakValidationMap<P>;
}
// 如果name是可选
interface Props {
name?: string;
desc: string;
}
const Test: React.FunctionComponent<Props> = (props) => {
return (
<div>
{props.name}
{props.desc}
</div>
);
};
Test.propTypes?.name; //name为可选的,则propTypes.name也是可选的
// name如果是必选项
interface Props {
name: string;
desc: string;
}
const Test: React.FunctionComponent<Props> = (props) => {
return (
<div>
{props.name}
{props.desc}
</div>
);
};
Test.propTypes?.name; //name是必选项,propTypes?.name也会转化为可选的
根据测试得知:propTypes属性类型是将泛型P转化为可选的
interface FunctionComponent<P = {}> {
defaultProps?: Partial<P>;
propTypes?: WeakValidationMap<P>;
}
type WeakValidationMap<T> = {
[K in keyof T]?: null extends T[K]
? Validator<T[K] | null | undefined>
: undefined extends T[K]
? Validator<T[K] | null | undefined>
: Validator<T[K]>
};
ts源码: 来看看它的实现方法
/**
* Make all properties in T optional
*/
type Partial<T> = {
[P in keyof T]?: T[P];
};
理解伪代码:
interface Props {
name: string;
desc: string;
}
type P = Props;
interface FunctionComponent<P = {}> {
defaultProps?: Partial<P>;
}
type Partial<T> = {
[P in keyof T]?: T[P];
};
类型 keyof T = name|desc
=》使用in循环属性name|desc
自己写个例子:
// 使用Partial<PersonInteface>后必填属性都变为可选的了
interface PersonInteface {
name: string;
age: number;
}
const person: Partial<PersonInteface> = {};
TS内置类型
Promise<T>
用法Promise<T>
// Promise<RespInterface>的用法, 是定义Promise返回的数据的类型为RespInterface
interface RespInterface {
name: string;
desc: string;
}
function Fetch<T>(params: object, method: string): Promise<RespInterface> {
return Promise.resolve({ name: "zhangsan", desc: "desc" });
}
Partial<T>
Partial<P>将传入的属性都变为可选属性
Rquired<T>
与Partial相反,将所有属性转换为必选
interface CountryInterface {
name: string;
population: number;
desc?: string;
}
type OneCountryType = Required<CountryInterface>;
type OtherRequired<T> = {
[P in keyof T]-?: T[P];
};
type OneCountryTypeOther = OtherRequired<CountryInterface>;
源码: -?: 表示必填
type OtherRequired<T> = {
[P in keyof T]-?: T[P];
}
源码:
type OtherReadonly<T> = {
readonly [P in keyof T]: T[P];
};
ReadOnly<T>
type t = Readonly<CountryInterface>;
源码:
type OtherReadonly<T> = {
readonly [P in keyof T]: T[P]
}
Pick<T,U>
- 取出接口
T中属性U的类型,组成一个新的类型 - U是单个类型或联合类型
- U keyof T
interface CountryInterface {
name: string;
population: number;
desc?: string;
}
type pType = Pick<CountryInterface, "name" | "population">;
let data: pType={};
Exclude<T,U>
- 与
Pick相反 - 从类型T中
剔除U属性,组成一个新的类型 - U keyof T
type testTypes = "a" | "b" | "c";
type TypeEXs = Exclude<testT, "c">; //U可以是联合类型;type TypeEXs = Exclude<testT, "c" | "a">;
const data :TypeEXs = 'a'; //data只能是'a'或'b';
Omit<T,U>
- 从对象类型中剔除某些类型
- 与
Exclude<T,U>类似,暂时没发现两则的区别
interface RespInterface {
name: string;
desc?: string;
population?: number;
a?: never;
}
type Types = Omit<RespInterface, "population">;
等价于
type Types = {
name: string;
desc?: string;
a?: never;
}