TypeScript的核心原则之一是对值所具有的结构进行类型检查
泛型
泛型是指一个表示类型的变量,一般用T表示,用它来代替某个实际的类型,而后通过实际调用时传入的类型来对其进行替换,可以利用泛型来适应不同类型。
TypeScript 中不建议使用 any 类型,不能保证类型安全,调试时缺乏完整的信息。
TypeScript可以使用泛型来创建可重用的组件。支持当前数据类型,同时也能支持未来的数据类型。扩展灵活。可以在编译时发现你的类型错误,从而保证类型安全。
1:泛型函数
<泛型变量名称>(参数1: 泛型变量, 参数2: 泛型变量, ...参数n: 泛型变量) => 泛型变量
/*------------基础使用方法------------*/
function join<T, P>(first: T, second: P): T {
return first;
}
//const twoParms = join<number, string>(1, '我是string');
const twoParms = join(1, '我是string');
/*---------泛型集合--------------*/
function map<T>(params: Array<T>) {
return params;
}
//const sanleType = map<string>(['123']);
const sanleType = map(['123']);
/* -----------泛型箭头函数-------------*/
const identity = <T,>(arg: T): T => {
return arg;
};
const identity2: <T>(arg: T) => T = (arg) => {
return arg;
};
2:泛型接口
/* -------------泛型接口-------------*/
interface ColumnProps<T> {
key: number | string;
title: string;
dataIndex: keyof T; // 约束 dataIndex 值需为引用泛型 T 中的属性
}
interface ITableItem {
key: number | string;
name: string;
address: string;
age: number;
}
const columns: Array<ColumnProps<ITableItem>> = [
{
title: '姓名',
dataIndex: 'name',
key: 'name',
},
];
3:泛型类
/*--------------泛型类---------------*/
class Person<T> {
love: T;
say: (arg: T) => T;
}
let myFn: IGeneric<number> = fn;
myFn(13); //13
let me = new Person<string>();
me.love = 'TS';
// me.love = 520; // ERROR
me.say = function(love: string){
return `my love is ${love}`;
}
泛型约束
泛型可以通过 extends 一个接口来实现泛型约束,写法如:
<泛型变量 extends 接口>
<T, K extends keyof T>
//K为传入的T上已知的属性,
interface IArray {
length: number
}
function logIndex<T extends IArray>(arg: T): void {
for (let i = 0; i < arg.length; ++i) {
console.log(i)
}
}
let arr = [1, 2, 3]
// logIndex<number>(arr) // 报错
logIndex<number[]>(arr) // 允许
logIndex(arr) // 自动类型推导,允许
泛型应用场景之一
/*-------------应用场景start---------------------------*/
interface ColumnProps<T> {
key: number | string;
title: string;
dataIndex: keyof T; // 约束 dataIndex 值需为引用泛型 T 中的属性
}
interface ITableItem {
key: number | string;
name: string;
address: string;
age: number;
}
interface TableProps {
dataSource: ITableItem[];
columns: Array<ColumnProps<ITableItem>>;
}
const MyTable = (props: TableProps) => {
const { dataSource, columns } = props;
return <Table dataSource={dataSource} columns={columns} />;
};
const ApplcationMod = () => {
const dataSource = [
{
key: '1',
name: '金城武',
age: 32,
address: '西湖区湖底公园1号',
},
{
key: '2',
name: '吴彦祖',
age: 42,
address: '西湖区湖底公园1号',
},
];
const columns: Array<ColumnProps<ITableItem>> = [
{
title: '姓名',
dataIndex: 'name',
key: 'name',
},
{
title: '年龄',
dataIndex: 'age',
key: 'age',
},
{
title: '住址',
dataIndex: 'address',
key: 'address',
},
];
return (
<div>
<h3>泛型应用场景</h3>
<MyTable dataSource={dataSource} columns={columns} />
</div>
);
};
keyof 关键字
keyof 关键字非常实用,后面可以看到很多工具泛型都是使用 keyof 实现的。
keyof T(索引类型查询)的结果为 T上已知的公共属性名的联合。
keyof 的一个特性: keyof T的类型会被认为是 string、number、symbol 的子类型。
interface Point {
x: number;
y: number;
}
type Axis = keyof Point;
// 等同于 type Axis = "x" | "y"
function cal(a: Point, b: Point, axis: Axis): number {
return (a[axis] + b[axis]) / 2;
}
cal({x:20,y:99},{x:100,y:200},'x')
type类型别名
1.可以创建联合类型、元组类型、基本类型(string,number,symbol)
//联合类型
type Pets = 'hi' | 'age' | 'hello';
interface Dog {
x:string
}
interface Cat {
y:number
}
type Pet = Dog | Cat
//元组+泛型
//与 声明数组类型 类似,只不过在 数组 基础上更加细分化每个元素的类型。
type Pair<T> = [T, T];
const ff: Pair<number> = [1, 2];
const cc: Pair<number> = [1, 1];
const dd: Pair<string> = ['1', '2'];
2.可以利用type和映射类型快速创建类型
{ [ K in P ] : T }
实例
type Coord = {
[K in 'x' | 'y']: number;
};
// 得到
// type Coord = {
// x: number;
// y: number;
// }
它执行了一个循环(可以理解为类似 for...in 的效果),这里的 P 直接设置为 'x' | 'y' 的一个联合类型,而 K 是一个标识符,它映射为 P 的每一个子类型。T 为数据类型,我们直接固定为 number,也可以是任何其他复杂类型。
//约束key,value为同样的值;
type Coord = { [K in 'x' | 'y']: K };
// 得到 type Coord = { x: 'x'; y: 'y'; }
3:生成类型的函数类型
可以封装一个快速生成接口类型的函数类型:
type Unite = 'dog' | 'cat' | 'pig';
type PetInfo = {
name: string;
age: number;
};
type Coord4 = {
[K in Unite]: PetInfo;
};
const animalInfo: Coord4 = {
dog: { name: 'dogname', age: 3 },
cat: { name: 'catname', age: 3 },
pig: { name: 'pigname', age: 3 },
};
//等同于
type Creat<K extends keyof any, U> = {
[P in keyof K]: U;
};
//预置的高级类型 Record<K extends keyof any, T> ,可以直接使用
type Pets = Creat<Unite,PetInfo>
const animalInfo2: Pets = {
dog: { name: 'dogname2', age: 3 },
cat: { name: 'catname2', age: 3 },
pig: { name: 'pigname2', age: 3 },
};
interface 与 type
相同点
/*都可以描述一个对象或者函数*/
interface User {
name: string
age: number
}
interface SetUser {
(name: string, age: number): void;
}
type User = {
name: string
age: number
};
type SetUser = (name: string, age: number)=> void;
*都允许拓展(extends)*/
interface Name {
name: string;
}
interface User extends Name {
age: number;
}
type Name = {
name: string;
}
type User = Name & { age: number };
//interface extends type
type Name = {
name: string;
}
interface User extends Name {
age: number;
}
//type extends interface
interface Name {
name: string;
}
type User = Name & {
age: number;
}
不同点
type 可以声明基本类型别名,联合类型,元组等类型
nterface 能够声明合并
interface User {
name: string
age: number
}
interface User {
sex: string
}
/*
User 接口为 {
name: string
age: number
sex: string
}
*/
不清楚什么时候用interface/type,能用 interface 实现,就用 interface , 如果不能就用 type 。
Partial
Partial 将传入的所有属性变为可选
// 映射类型进行推断
type Partial<T> = {
[P in keyof T]?: T[P];
};
interface Article {
articleId: string;
title: string;
content: string;
status: number;
}
//等同于
// interface Article1 {
// articleId?: string;
// title?: string;
// content?: string;
// status?: number;
// }
const Index = (props: Partial<Article>) => {
//to do..........
};
Required(用处不大)
将传入的所有属性变为必选项,这个和 Partial 相反
type Required<T> = { [P in keyof T]-?: T[P] };
interface Article {
articleId: string;
title: string;
content: string;
status: number;
}
const Index = (props: Required<Article>) => {
//to do..........
};
Exclude
Exclude<T, U> -- 从T中剔除可以赋值给U的类型,起到过滤的作用
//官方例子
type T00 = Exclude<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "b" | "d"
需求:工作日时间可调整。
type Exclude<T, U> = T extends U ? never : T;
//平常日剔除休息日就是工作日
// 一周(平常日)
type Weekday = 'Monday' | 'Tuesday' | 'Wednesday' | 'Thursday' | 'Friday' | 'Saturday' | 'Sunday';
// 休息日
type DayOff = 'Saturday' | 'Sunday'|'Monday';
// 工作日
type WorkDay = Exclude<Weekday, DayOff>;
const day: WorkDay = 'Tuesday';
TypeScript 的映射类型
简单语法
{ [ K in P ] : T }
type Coord = {
[K in 'x' | 'y']: T;
};
// 得到
// type Coord = {
// x: T;
// y: T;
// }
首选确定它执行了一个循环(可以理解为类似 for...in 的效果),这里的 P 直接设置为 'x' | 'y' 的一个联合类型,而 K 是一个标识符,它映射为 P 的每一个子类型。T 为数据类型,我们直接固定为 number,也可以是任何其他复杂类型。
因为 T 值数据类型可以是任何值,甚至数值 1 也可以,因此我们把数据类设成 K 自身也行:
type Coord = { [K in 'x' | 'y']: K };
// 得到
type Coord = { x: 'x'; y: 'y'; }
立用映射类型+keyOf关键字 封装一个快速生成接口类型的函数类型Record
//封装一个快速生成接口类型的函数类型
type Record<K extends keyof any, T> = {
[P in K]: T;
};
//K extends keyof any
等价于
//type K = 'dog' | 'cat' | 'fish';
type PetType = 'dog' | 'cat' | 'fish';
interface PetInfo {
name: string;
age: number;
}
type Pets = Record<PetType, PetInfo>;
const pets: Pets = {
dog: { name: 'didi', age: 1 },
cat: { name: 'cici', age: 2 },
fish: { name: 'fifi', age: 3 }
};
or
interface Pets {
dog: PetInfo;
cat: PetInfo;
fish: PetInfo;
}
额外的属性检查
需求:代码迁移复用,希望原先的 js 代码能够迁移到 ts 中,增加类型推断
1.使用 as 关键字,断言类型,
as 语法,大部分其他场景应该避免使用,类型断言纯粹是编译时语法。类型推断应该尽量使用 interface / type / 基础类型
//const asFoo = {};//报错写法
//const asFoo = {} as Foo;
const asFoo: { [propName: string]: any } = {};//推荐字符串索引用法
asFoo.bar = 2;//error类型“{}”上不存在属性“bar”。
asFoo.title = 'as 类型断言';//error类型“{}”上不存在属性“bas”。
interface Foo {
bar: number;
title: string;
[propName: string]: any;//可解决报错
}
类型保护
联合类型时,会遇到这种问题。无法准确知道是否存在某个属性。
类型注解&类型推段
TS能够自动的分析变量类型时,就什么都不需要做了
TS无法分析出变量类型时,就需要使用类型注解。
定义基础类型可不用写类型注解
//let count:number = 1;
let count = 1;//不需要写类型注解;
const fistCount = 1;
const secondCount = 2;
const total = fistCount + secondCount;//不需要加类型注解,ts可直接推断出total为number类型
//返回值类型不写也不会报错,但是实战中一般还是要做一层约束。
function getTotal(fistCount:number,secondCount:number):number{
return fistCount + secondCount;
}
const total = getTotal(1,2);//不需要加类型注解,ts分析出是number类型