TS 的基础概念 什么是 TS
JS 的一个超集,在原有的语法基础上,添加强类型并切换为基于类的面向对象语言
面向项目 TS - 面向解决大型的复杂项目、架构、代码维护复杂场景 JS - 脚本化语言,用于面向简单页面场景
自主检测 TS - 编译时,主动发现并纠正错误 JS - 运行时,执行报错
类型检测 TS - 强类型语言,支持动态和静态的类型检测 JS - 弱类型语言,无静态类型选项
运行流程 TS - 依赖编译,依靠编译打包实现在浏览器端的运行 JS - 可直接在浏览器端运行
复杂特性 TS - 模块化、接口、泛型
TS 基础类型和语法
boolean、string、number、array、null、undefined、tuple
1 2 3 4 5 6 7 8 9 let isEnable : boolean = false ;let className : string = "baidu" ;let classNum : number = 2 ;let u : undefined = undefined ;let n : null = null ;let classArr : string [] = ["basic" , "execute" ];let classArr : Array <string > = ["basic" , "execute" ];let tupleType : [string , boolean ] = ["basic" , false ];
enum 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 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 enum Score { BAD , NG , GOOD , } let score : Score = Score .BAD ;enum Score { BAD = "BAD" , NG = "NG" , GOOD = "GOOD" , } enum Score { BAD , NG , GOOD , } let scoreName = Score [0 ]; let scoreVale = Score ["BAD" ]; enum Enum { A, B, C = "C" , D = "D" , E = 8 , F, } let Enum ;(function (Enum ) { Enum ["A" ] = 0 ; Enum ["B" ] = 1 ; Enum ["C" ] = "C" ; Enum ["D" ] = "D" ; Enum ["E" ] = 8 ; Enum ["F" ] = 9 ; Enum [0 ] = "A" ; Enum [1 ] = "B" ; Enum [8 ] = "E" ; Enum [9 ] = "F" ; })(Enum || (Enum = {}));
object / Object / {} 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 interface ObjectConstructor { create (o : object | null ): any ; } const proto = {};Object .create (proto);Object .create (null );Object .create (undefined ); interface Object { constructor : Function ; toString (): string ; toLocaleString (): string ; valueOf (): Object ; } interface ObjectConstructor { new (value : any ): Object ; readonly prototype : Object ; } const obj = {};obj.prop = "props" ; obj.toString ();
接口 - interface
对行为模块的抽象,具体的行为是由类来实现
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 interface Class { name : string; time : number; } let baidu : Class = { name : 'typescript' , time : 2 } interface Class { readonly name : string; time : number; } let arr : number[] = [1 , 2 , 3 , 4 ];let ro : ReadonlyArray <number> = arr;ro[0 ] = 12 ; ro.push (5 ); ro.length = 10 ; arr = ro; interface Class { readonly name : string; time : number; [propName : string]: any; } const c1 = { name : "JS" };const c2 = { name : "browser" , time : 1 };const c3 = { name : "ts" , level : 1 };
interface vs type
type 不能被继承或者实现 (& 可不用理解为继承) ,interface 可以被继承或者实现,但可以合并
type 可以定义任何类型,包括联合类型、交叉类型、字面量类型、原始类型等。interface 只能定义对象类型,包括属性、方法、索引等
type 通常用于为复杂类型创建别名,以方便在代码中使用。interface 通常用于定义某个实体的结构,以及实现该结构的对象或类
交叉类型 - & 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 36 37 38 39 40 41 42 43 44 45 interface A { inner : D; } interface B { inner : E; } interface C { inner : F; } interface D { d : boolean ; } interface E { e : string ; } interface F { f : number ; } type ABC = A & B & C;let abc : ABC = { inner : { d : false , e : "className" , f : 5 , }, }; interface A { c : string ; d : string ; } interface B { c : number ; e : string ; } type AB = A & B;let ab : AB ;
断言 - 类型的声明和转换
编译时作用
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 36 let anyValue : any = 'hi baidu' ;let anyLength : number = (<string >anyValue).length ;let anyValue : any = 'hi baidu' ;let anyLength : number = (anyValue as string ).length ;type ClassTime = () => number ;const start = (ClassTime : ClassTime | undefined ) { let time = classTime!(); } const tsClass : number | undefined = undefined ;const baidu : number = tsClass!;console .log (baidu);const tsClass = undefined ;const baidu = tsClass;console .log (baidu); let score!: number ;startClass ();console .log ('' + score); function startClass ( ) { score = 5 ; }
类型守卫 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 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 interface Teacher { name : string ; courses : string []; } interface Student { name : string ; startTime : Date ; } type Class = Teacher | Student ;function startCourse (cls : Class ) { if ('courses' in cls) { console .log ("Courses:" + cls.courses ); } if ('startTime' in cls) { console .log ("startTime:" + cls.startTime ); } } function class (name : string , score : string | number ) { if (typeof score === "number" ) { return "teacher:" + name + ":" + score; } if (typeof score === "string" ) { return "student:" + name + ":" + score; } } const getName = (cls : Class ) => { if (cls instanceof Teacher ) { return cls.courses ; } if (cls instanceof Student ) { return cls.startTime ; } } const isTeacher = function (cls : Teacher | Student ): cls is Teacher { return 'courses' in cls; } const getName = (cls : Teacher | Student ) => { if (isTeacher (cls)) { return cls.courses ; } }
never 的作用
类型收缩、检查错误
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 type Method = 'GET' | 'POST' ; function request (method: Method, url: string ) { switch (method) { case 'GET' return 1 ; case 'POST' return 2 ; default const n : never = method; return n; } } function errorGen (msg: string ): never { throw new Error (msg); } function infiniteLoop ( ): never { while (true ) { } }
any、unknown
any 和 unknown 都是顶级类型,但是 unknown 更加严格,不像 any 那样不做类型检查
unknown 因为未知性质,不允许访问属性,不允许赋值给给 any 和 unkown 之外的类型变量
联合类型中的 unkown 为 unkown,意外是 any 类型。如果至少一种组成类型是 any,联合类型会相当于 any
交叉类型中的 unkown 为其他
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 let foo : any = 123 ;console .log (foo.msg ); let a_value1 : unknown = foo; let a_value2 : any = foo; let a_value3 : string = foo; let bar : unknown = 222 ; console .log (bar.msg ); let k_value1 : unknown = bar; let K_value2 : any = bar; let K_value3 : string = bar; type UnionType1 = unknown | null ; type UnionType2 = unknown | undefined ; type UnionType3 = unknown | string; type UnionType4 = unknown | number[]; type UnionType5 = unknown | any; type IntersectionType1 = unknown & null ; type IntersectionType2 = unknown & undefined ; type IntersectionType3 = unknown & string; type IntersectionType4 = unknown & number[]; type IntersectionType5 = unknown & any;
关键字解释 typeof
用于类型表达时,用于从一个变量上获取它的类型
1 2 3 4 5 6 7 8 9 const func = (a : number , b : number ): number => { return a + b; }; const obj = { name : "jack" , age : 11 , }; type tupeFunc = typeof func; type obj = typeof obj;
keyof
将一个类型映射为它所有成员名称的联合类型
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 interface TsInterfaceObject { first : string ; second : number ; } type TsTypeObject = { first : string ; second : number ; }; type TsTypeAlias = string ;class JsClass { private priData : number ; private priFunc ( ) {} public pubData : number ; public pubFunc1 ( ) {} public pubFunc2 ( ) {} } type NameUnionOfInterface = keyof TsInterfaceObject ; type NameUnionOfTypeObj = keyof TsTypeObject ; type NameUnionOfTypeAlias = keyof TsTypeAlias ; type NameUnionOfClass = keyof JsClass ;
in
类似 for..in
遍历,[p in K] 相当于在遍历 K 的属性
extends 类型继承
interface 可用 extends 继承,type 不可以,可以同时继承多个 interface,它们之间用逗号分隔
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 interface IHuman { gender : "male" | "female" ; age : number ; } interface IProgrammer { language : "java" | "php" | "javascript" ; } interface IWebDeveloper extends IProgrammer , IHuman { skill : "vue" | "react" ; } const Me : IWebDeveloper = { skill : "react" , language : "javascript" , age : 18 , gender : "male" , };
类型约束
在书写泛型的时候,往往需要对类型参数作一定的限制
1 2 3 4 function getCnames<T extends { cname : string }>(entities : T[]): string [] { return entities.map ((entity ) => entity.cname ); }
条件匹配 1 2 3 4 5 6 7 type OnlyWantNumber <T> = T extends number ? any : never ;type Type1 = OnlyWantNumber <number >; type Type2 = OnlyWantNumber <string >; type P<T> = T extends "x" ? string : number ;type A3 = P<"x" | "y" >;
infer
可以在 extends 关键字右侧的类型表达式中的任何位置使用,为出现在该位置的任何类型命名
1 2 3 type Unpack <A> = A extends Array <infer E> ? E : A;type Test = Unpack <Apple []>; type Test = Unpack <Apple >;
常用语法实现 Partial
将 T 中所有属性都变为 非必须的
1 2 3 4 5 type MyPartial <T> = { [P in keyof T]?: T[P]; };
Required
将 T 中所有属性都变为 必须的
1 2 3 4 5 type MyRequired <T> = { [K in keyof T]-?: T[K]; };
ReadOnly
将 T 中所有属性都变成 仅读的
1 2 3 type MyReadonly <T> = { readonly [K in keyof T]: T[K]; };
Record
构造一个键类型属于 K(某种类型或集合),值的类型为 V 的新类型
1 2 3 4 5 type MyRecord <K extends keyof any , V> = { [P in K]: V; };
Pick
从 T 中取出
一部分属性 K 组成一个新的类型返回
1 2 3 4 type MyPick <T, K extends keyof T> = { [P in K]: T[P]; };
Omit
从 T 中剔除
一部分属性 K 组成一个新的类型返回
1 2 3 4 type MyOmit <T, K extends keyof any > = Pick <T, Exclude <keyof T, K>>;
Exclude
将 U 联合类型的所有成员,从类型 T 中排除,可以理解为取差集
1 2 type MyExclude <T, U> = T extends U ? never : T;
选取 T 类型和 U 类型两者的公共部分并返回为一个新类型,可以理解为取交集
1 2 type MyExtract <T, U> = T extends U ? T : never ;
NonNullable
从 T 中剔除 null | undefined 类型
1 type MyNonNullable <T> = T extends null | undefined ? never : T;
Parameters
基于一个函数参数的类型构造一个元组类型(tuple type)。所以这个工具类型的作用就是获取函数参数的类型
1 type MyParameters <T extends (...args : any ) => void > = T extends (...args : infer P) => void ? P : never ;
ConstructorParameters
把构造函数的参数类型作为一个元组类型返回
1 type MyConstructorParameters <T extends new (...args : any ) => any > = T extends new (...args : infer P) => any ? P : never
TS 进阶 函数重载 1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Course { start (name : number , score : number ): number ; start (name : string , score : string ): string ; start (name : string , score : number ): string ; start (name : number , score : string ): string ; start (name : Combinable , score : Combinable ) { if (typeof name === "string" || typeof score === "string" ) { return "student:" + name + ":" + score; } } } const course = new Course ();course.start ("yunyin" , 5 );
泛型
让模块可以支持多种类型数据 - 让类型声明和值一样,可以被赋值和传递
1 2 3 4 5 6 7 function startClass<T, U>(name : T, score : U): T { return name + score; } console .log (startClass<String , Number >("yunyin" , 5 ));
获取对象键值约束 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 const cat = { name : 'kitty' , age : 3 , love : 'flower' } const user = { loginId : 'abc' , loginPwd : '123456' } function getValue<T extends object, K extends keyof T>(obj : T, name : K): T[K] { return obj[name]; } type BanType <T, E> = T extends E ? never : T function log<T>(x : BanType <T, Date >) {}
装饰器 - decorator 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 function Baidu (target : Function ): void { target.prototype .startClass = function ( ): void { } } function propsWrapper (target : Object , key : string ) { Object .defineProperty (target, key, { }) } @Baidu class Course { constructor ( ) { } @propsWrapper public name : string ; @methodDec }
使用 typeScript 引入和使用 webpack 打包配置
vue-cli - vue init/create ${myProject} ${template} => 配置 webpack => 编译时
a. entry - 入口 b. extentions 加上 ts 文件 area - 用于处理尝试的数据尾缀列表 c. loaders - ts-loader,增加对于 ts 的处理 => 工程化
TS 配置
tsconfig.json
vue / vuex + typescript 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 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 <template> <div > <vueComponent /> </div > </template> <script lang="ts"> // 1. 定义组件的方式上: 形式上 - extends // const Component = { // TS无法断定内部为vue组件,需要额外做申明处理 - Vue.prototype.xxxx // } // 申明当前组件模块 Vue.component or Vue.extend import Vue from 'vue' const Component = Vue.extend({ // 类型推断 }) // 2. 全面拥抱面向对象 - 官方vue-class-component import Component from 'vue-class-component' // @Component 本质 —— 类装饰器 => 利用装饰器,统一描述vue模板等概念 @Component({ template: '<vueComponent />' }) export default class myComponent extends Vue { message: string = 'Hello' onClick(): void { console.log(this.message); } } // 3. 申明 - 利用ts的额外补充模块declare => 实现独立模块的声明,使之可以被独立引用 declare module '*.vue' { import Vue from 'vue' export default Vue } // 补充模块 - 通常使用.d.ts来做申明描述 declare module '/typings/vuePlugin.d.ts' { interface Vue { myProps: string } } // 实例中使用 let vm = new Vue() console.log(vm.myProps) // 4. props - 提供propType原地声明复合变量 import { propType } from 'vue' interface customPayload { str: string, number: number, name: string } const Component = Vue.extend({ props: { name: String, // 字符串类型 success: { type: String }, // 普通对象类型 payload: { type: Object as propType<customPayload> }, callback: { type: Function as propType<() => void> } } }) // 5. computed 以及 method中包含this且有return的方法 需要声明返回类型 computed: { getMsg(): string { return this.click() + "!" } } methods: { click(): string { return this.message + 'baidu' } } // 6. vuex的接入ts - 声明使用 // vuex.d.ts 声明模块 - ComponentCustomProperties import { ComponentCustomProperties } from 'vue' declare module '@vue/runtime-core' { interface State { count: number } interface ComponentCustomProperties { $store: Store<State> } } // 7. api形式编码实现 - 官方推荐 // store.ts import { InjectionKey } from 'vue' import { createStore, Store } from 'vuex' export interface State { count: number } export const key: InjectionKey<Store<State>> = Symbol() export const store = createStore<State>({ state: { count: 0 } }) // ####################### // main.ts import { createApp } from 'vue' import { store, key } from './store' const app = createApp({ // 传入参数 }) // 利用了provider & inject app.use(store, key) // => 传入injection Key => vue高级使用里会提到vue.use app.mount('#app') // ######################## // 消费方 import { useStore } from 'vuex' import { key } from './store' export default { const store = useStore(key) store.state.count } // 标准接口形式的功能引入,核心利用了vue的provide&inject // 8. vuex面向对象 - 使用vuex-class import { State, Action, Getter } from "vuex-class" export default class App extends Vue { // 属性装饰器,整合了store状态 @State login: boolean; // 事件装饰器,整合了store方法 @Action setInit: () => void; get isLogin: boolean; mounted() { this.setInit(); } } </script>
参考资料 TypeScript 体操