TS
正经人谁学TS啊
发展历史
- 2012-10:微软发布了TypeScript第-个版本(0.8)
- 2014-10:Angular2发布了基于TypeScript的2.0版本
- 2015-04:微软发布了Visual Studio Code
- 2016-05:@types/react?发布,TypeScript可开发React
- 2020-09:ue发布了3.0版本,官方支持TypeScript
- 2021-11:v4.5版本发布
为什么TS
和JS的比较
JS |
TS |
动态类型 |
静态类型 |
弱类型语言 |
弱类型语言 |
动态类型
在执行阶段才确定类型的匹配。编译在执行时
静态类型
提前确定类型匹配。编译在执行前
弱类型语言
数据类型可以被忽略的语言=>一个变量可以赋不同的数据类型的值。
强类型语言
强制数据类型定义的语言=>一个变量被指定了某个数据类型,如果不经过强制转换,永远是这个类型=>一个类型的变量不会被当成另一种类型处理,(强类型定义语言是类型安全的语言)
TS优势
静态类型
- 可读性增强:基于语法解析TSDoc,ide增强
- 可维护性增强:在编译阶段暴露大部分错误=>多人合作的大型项目中获得更好的稳定性和开发效率
JS的超集
- 包含于兼容所有的JS特性,支持共存
- 支持渐进式引入的升级
基本语法
基础数据类型
JS
1 2 3 4 5
| const q = 'string'; const w = 1; const e = true; const r = null; const t = undefined;
|
TS
1 2 3 4 5
| const q: string = 'string'; const w: number = 1; const e: boolean = true; const r: null = null; ocnst t: undefined = undefined;
|
对象类型
TS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| const bytedancer: IBytedancer = { jobId: 9303245, name: 'Lin', sex: 'man', age: 28, hobby: swimming', }
/*自定义对象类型:I开头表示是个类型,与普通对象/类的作区分*/ interface IBytedancer { /*只读属性:约束属性不可在对象初始化外赋值*/ readonly jobId: number; name: string; sex: 'man'|'woman'|'other'; age: number; /*可选属性:定义该属性可以不存在*/ hobby?: string; /*任意属性:约束所有对象属性都必须是该属性的子类型*/ [key: string]: any; }
/*报错:无法分配到"jobId”,因为它是只读属性*/ bytedancer.jobId = 12345; /*成功:任意属性标注下可以添加任意属性*/ bytedancer.plateform = 'data'; /*报错:缺少属性"name",hobby可缺省*/ const bytedancer2: IBytedancer = { jobId: 89757, sex: 'woman', age: 18, }
|
函数类型
JS
1 2 3 4
| function add(x, y) { return x + y; } const mult = (x, y) => x * y;
|
TS
1 2 3 4 5 6 7 8 9
| function add(x: number, y: number): number { return x + y; } const mult: (x: number, y: number) => number = (x, y) => x * y; ↓更加清晰 interface IMult { (x: number, y: number): number; } const mult: IMult = (x, y) => x * y;
|
函数重载
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| function getDate(type:'string',timestamp?:string):string; ↓ interface IGetDate { (type:'string',timestamp?:string):string; (type:'date',timestamp?:string):Date; (type:'string'|'date',timestamp?:string):Date string; }
const getDate2:IGetDate = (type, timestamp) => { const date = new Date(timestamp); return type ==='string' ? date.toLocalestring():date; } 也就是说,函数里面的函数的类型范围要小于函数本身所在的类型范围。
|
数组类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| type IArr1 = number[]; type IArr = string[];
type IArr2 = Array<string | number | Record<string,number>>;
type IArr3 = [number, number, string, string];
interface IArr4 { [key: number]: any; }
const arr1: IArr1=[1,2,3,4,5,6]; const arr2: IArr2=[1,2,'3','4',{a:1}]; const arr3: IArr3=[1,2,'3','4']; const arr4: IArr4 ['string',() => null, {}, []];
|
TS补充类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| type IEmptyFunction = () => void;
type IAnyType = any;
enum EnumExample { add = '+', mult = '*', } EnumExample['add'] === '+'; EnumExample['*'] === 'mult';
enum ECorlor { Mon, Tue, Wed, Thu, Fri, Sat, Sun }; ECorlor['Mon'] === 0; ECorlor[0] === 'Mon';
type INumArr = Array<number>;
|
TS泛型
特征:不预先指定类型的时候,也就是在使用某类型时,才确定该类型。
使用
1 2 3 4 5 6 7 8 9
| function getRepeatArr(target) { return new Array(100).fill(target); }
type IGetRepeatArr = (target: any) => any[];
type IGetRepeatArrR = <T>(target: T) => T[];
|
1 2 3 4 5 6 7 8 9 10 11 12 13
|
interface IX<T,U> { key: T; val: U; }
class IMan<T> { instance: T; }
type ITypeArr<T> = Array<T>;
|
有点高级的语法
1 2 3 4 5 6
| 函数的泛型
type IGetRepeatStringArr = <T extends string>(target: T) => T[]; const getstrArr: IGetRepeatStringArr = target => new Array(100).fill(target);
getstrArr(123);
|
1 2 3 4 5 6
| 类型别名的泛型
type IGetRepeatArr<T = number>(target: T) => T[]; const getRepeatArr: IGetRepeatArr target = new Array(100).fill(target);
getRepeatArr('123')
|
类型别名&类型断言
这段代码是使用TypeScript定义了一个函数”keyBy”,它用于将一个对象数组转换为一个键值对字典。
第1行:通过”type”关键字定义了一个别名类型”IObjArr”,它代表了一个对象数组,每个元素都是一个包含key属性与任意其他属性的对象。
第3行:声明了一个名为”keyBy”的函数,它参数为”objArr”,它类型为Array,T继承自IObjArr,意味着objArr的元素必须是包含key属性的对象。
第5行:定义了一个名为result的变量,初始值是将objArr使用reduce方法转换为的对象,reduce方法会接收一个回调函数,对数组进行遍历,不断累积最终的结果。
第9行:使用”as”关键字断言result的类型为Record<string,T>,意味着result是一个以字符串为键,T类型为值的字典。
最后,返回result。
1 2 3 4 5 6 7 8 9 10 11 12 13
| type IObjArr = Array<{ key: string; [objKey: string]: any; }> function keyBy<T extends IObjArr>(objArr: Array<T>){ const result = objArr.reduce((res, val, key) => { res[key] = val; return res; },{});
return result as Record<string,T>;
|
字符串/数字 字面量
1 2 3 4 5 6 7
|
type IDomTag = 'html' | 'body' | 'div' | 'span';
type IOddNumber = 1 |3 | 5 | 7 | 9;
|
高级类型
来自chatGPT
一些常用类型:
- 可读属性
- 可选属性
- 索引签名
- 字面量类型
常用的 TypeScript 高级类型有:
交叉类型 (Intersection Types)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| interface User { id: number; name: string; }
interface Role { role: string; }
type UserWithRole = User & Role;
const userWithRole: UserWithRole = { id: 1, name: 'John Doe', role: 'admin' };
|
联合类型 (Union Types)
1 2 3 4 5 6 7
| type UserID = number | string;
const userID: UserID = 123; const anotherUserID: UserID = 'abc';
const wrongUserID: UserID = true;
|
类型别名 (Type Aliases)
映射类型 (Mapped Types)
条件类型 (Conditional Types)
可辨识联合类型 (Discriminated Unions)
类型推断 (Type Inference)
类型保护 (Type Guards)
索引类型 (Index Types)
联合类型推断 (Union Type Inference)
1.联合/交叉类型
为书籍列表编写类型
1 2 3 4 5 6 7 8 9
| const bookList = [{ author: 'xioaming', type: 'history', range: '2001-2021' },{ author: 'xiaoli', type: 'story', theme: 'love', }]
|
↓类型声明繁琐,存在较多重复
1 2 3 4 5 6 7 8 9 10 11
| interface IHistoryBook { author: string; type: string; range: string; } interface IStoryBook { author: string; type: string; theme: string; } type IBookList = Array<IHistoryBook | IStoryBook>;
|
改进后
- 联合类型:IA | IB 联合类型表示一个值可以是几种类型之一
- 交叉类型:IA & IB 多种类型叠加到一起形成的一种类型,包含了所有所需的所有类型特性
1 2 3 4 5 6 7 8 9
| type IBookList = Array<{ author: string; } & ({ type: 'history'; range: string; } | { type: 'story'; theme: string; })>
|
2.类型保护&类型守卫
原型
1 2 3 4 5 6 7 8 9 10 11 12 13
| interface IA {a: 1, a2: 2} interface IB {b: 1, b1: 2}
function log(args: IA | IB) { if(arg.a) { console.log(arg.a1); } else { console.log(arg.b1); } }
|
改造
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| interface IA a:1,al:2 interface IB b:1,b1:2
/*类型守卫:定义一个函数,它的返回值是一个类型谓词,生效范围为子作用域*/ function getIsIA(arg: IA | IB): arg is IA { return !!(arg as IA).a; }
function log2(arg: IA | IB) { if (getIsIA(arg)){ console.log(arg.a1) } else { console.log(arg.b1); } }
|
实现函数logBook类型
1 2 3 4 5 6 7 8 9 10
| function logBook(book: IBookItem) { if(book.type === 'history') { console.log(book.range); } else { console.log(book.theme); } }
|
3.merge函数类型
JS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
function merge1(sourceObj, targetObj){ const result {...sourceobj }; for(let key in targetobj) { const itemVal = sourceobj[key]; itemVal && (result[key] = itemVal); } return result; } function merge2(sourceObj, targetObj){ return {...sourceobj,...targetobj }; }
|
TS
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| interface ISourceobj { x?: string; y?: string; }
interface ITargetobj { x: string; y: string; } type IMerge = (sourceobj: ISourceobj, targetobj: ITargetobj) => ITargetobj;
|
改进
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| interface IMerge { <T extends Record<string,any>>(sourceObj: Partial<T>, targetObj: T): T; }
type IPartial<T extends Record<string, any>> = { [P in keyof T]?: T[P]; } type IKeys = keyof { a: string; b: number };
|
解释说明
这段代码是一个定义了多个TypeScript类型的代码。
IMerge是一个接口,它定义了一个方法,该方法接受两个对象,并返回一个新的对象,其中第一个对象是Partial类型,第二个对象是T类型,T是Record类型的泛型。
IPartial是一个类型别名,它定义了T类型的可选部分,T是Record类型的泛型。
IKeys是一个类型别名,它定义了一个名为’a’和’b’的对象的键的类型,其中’a’的类型是字符串,’b’的类型是数字。
关系:
IMerge
是一个接口,它定义了一个合并两个对象的方法,并将合并后的对象返回。它接受两个对象作为参数,分别是源对象和目标对象,返回值为目标对象。
IPartial
是一个类型别名,它定义了一个可选字段的对象。也就是说,可以有一些字段是不是必填的。
IKeys
是一个类型别名,它定义了一个字符串字面量类型的联合类型。它的值是 ‘a’ 和 ‘b’。
4.函数返回值类型
1 2 3 4 5 6 7 8 9 10 11
|
function delayCall(func) { return new Promise(resolve => { setTimeout(() => { const result = func(); resolve(result) }, 1000); }); }
|
↓怎么写个类型声明嘞?
1 2 3 4 5 6 7 8 9 10
| type IDelayCall = < T extends () => any >(func: T) => ReturnType<T>; type IReturnType< T extends (...args: any) => any > = T extends (...args: any) => infer R ? R : any;
|
解释说明
这段 TypeScript 代码定义了两种类型:IDelayCall
和 IReturnType
.
IDelayCall
是一个泛型函数,接受一个类型为 (...args: any) => any
的参数func
,返回值为 func
的返回类型(使用了 TypeScript 的 ReturnType
内置类型)。
IReturnType
也是一个泛型函数,它接受一个类型为 (...args: any) => any
的参数,并使用 TypeScript 的 infer
关键字来推断该函数的返回类型,如果返回类型不能被推断出来,则默认为any
类型。
这两个类型定义之间是有关系的。
因此,我们可以将 IReturnType
定义为 IDelayCall
函数的内部类型,以便进行更简洁、清晰的代码编写。
工程运用
浏览器Web
webpack
- 配置webpack loader相关配置(处理ts文件,转化成js文件)
- 配置tsconfig.js文件
- 运行webpack启动/打包
- loader处理ts文件时,会进行编译与类型检查
相关loader:
- awesome-typescript-loader
- babel-loader
NodeJs
使用TSC编译
- 安装node npm
- 使用npm安装tsc
- 配置tsocnfig.js文件
- 使用tsc运行编译得到的js文件