typeScript

概述

  • 安装:npm install -g typescript
  • 查看版本:
    • tsc -v
  • 把ts文件编译为js:
    • tsc xxx.ts
  • 初始化规则配置文件:
    • tsc --init
    • 会生成tsconfig.json文件
  • 基本数据类型
    • boolean string number null undefined
  • 类型检测 typeof
  • 类型推断:当声明并初始化变量时,如果不指定类型,编译器会根据初始值进行类型推导
const boo:boolean = true;
const str:string = 'aa';
const num:number = 10;
const n:null = null;
const un:undefined = undefined;
const an:any = '';//任意类型
const unk:unknown = '';//未知类型

//数组
const arr:number[] = [1,2,3];
const arr2:Array<number> = [1,2];
//元组-指定数组中类型的顺序
const person:[string,number] = ['name', 18];
//枚举
enum color = {red, black, white};//0 1 2
// enum color = {red, black = 5, white};//0 5 6

//空类型
function fn():void{}
//永不发生时的类型描述
function fn():never{}

//类型断言
let x:unknown;
const leng:number = (x as string).length;
// const leng:number = (<string>x).length;//jsx中不支持这种写法

//联合类型
let numStr:number|string = 1;

// 接口
interface IPerson {
    name:string
    age:number
    num?:number//可选属性
    readonly id:number//只读属性,只能初始化数据,不能修改
    fn: (a:number) => void//函数属性
}
//索引类型接口
interface Ip2 {
    [key:number]:any
}
interface Ip2 {
    [index:number]:string//数字索引类型
}
const arr: Ip2 = ['a','b','c'];

//函数类型
interface IFn {
    [a:number,b:number]: boolean
}
const fn: IFn = (a:number, b:number) => {
    return a > b;
}

//描述类的类型
interface IPerson {
    name:string
    age:number
    say:()=>void
}
class Person implements Iperson {
    name:string
    age:number
    constructor(name:string,age:number){
        this.name = name
        this.age = age
    }
    say(){}
}

类的权限管理-修饰符

  • public 公共的,未声明权限的成员默认是public,类实例可以访问
  • private 私有的 只能在类中被访问
  • protected 受保护的 类以及子类中可以被访
  • readonly 只读的
//public特殊用法
class Test2 {
    constructor(public name, public fn){//相当于 this.name=name;this.fn=fn
        
    }
}

函数

  • 可选参数
  • 剩余参数
  • 函数重载
//函数重载
function fn(str:string):string;
function fn(num:number):number;
function fn(param:string|number):string|number {
    console.log(param)
};
fn('aa')
fn(10)

//类型守卫
class A{
    run(){}
}
class B{}
function test(p: A | B): p is A{
    if('run' in p){//in 关键字用于判断对象中是否有某一个成员
        p.run()
    }
    return p instanceof A;
}

接口的继承

  • 接口可以多继承
interface IA {}
interface IB {}
interface IC extends IA,IB {}
class CA implements Ic {}

//接口继承类,一个接口继承一个类后,会继承类中所有的成员,单不会去实现,需要接口的实现类去实现所有功能
class CB {}
interface ID extends CB {}
class CC extends CB implements ID {}

泛型

  • 泛型也相当于占位符类型,或者类型变量
  • 解决类型丢失的问题
  • 提供代码的复用性和灵活度
  • 泛型约束
    • 关键字 extends
    • 约束对泛型传入的类型必须具有某些属性
  • 泛型new
    • 使用 new()=> 类 ,来规定传入一个类作为参数
//函数中使用泛型
function fn<T,K>(a: T, b: K): [T, K]{
    return [a, b];
}
const res = fn<string>('aa');
const res = fn<number>(10);

//接口中使用泛型
interface IA<T, K> {
    name: T,
    age: K
}
const obj:IA<string, number> = {
    name: 'a', age: 18
}

//在类中使用泛型
class CA<T> {
    private data: T[] = []
    add(item: T){
        this.data.push(item)
    }
    getDataItem(index:number):T {
        return this.data[index];
    }
}
const obj = new CA<string>();
obj.add('aa')

//泛型约束
interface IB {
    length: number
}
function fn2<T extends IB>(a: T): T {
    console.log(a.length);
    return a;
}
fn2("aa");//类型推断后->fn2<string>("aa") 正确,因为字符串有length属性
// fn2<number>(10);//错误,因为数字有length属性

//泛型重载,有多个重载时,在类型匹配时会从上往下按顺序匹配,只要匹配成功就不再匹配后面的类型
function fn3<T>(data: T): Array<T>;//01
function fn3<T>(data: Array<T>): Array<T>;//02
function fn3(data: any){
    if(Array.isArray(data)){
        return data;
    }
    return [data];
}
// const res = fn3(['a','b']); 这如不指定泛型具体类型,根据类型推断得到的会是个二维数组的类型string[][],因为先识别的是01重载函数
const res = fn3<string>(['a','b']);//指定类型后,会走02重载函数,返回类型为string[]
const res = fn3<number>(10);

//泛型的多种写法
const fn4: <T>(data: T) => T = (data)=>{return data}
const fn5: {<T>(data: T): T} = (data)=>{return data}
interface IC<T> { (data: T): T };
const fn6:IC<string> = (data)=>{return data}

//泛型new
const create = <T>(c:new()=>T):T=>{//参数必须是类
    return new c()
}
interface ID {}
class CB implements ID {}
// const iobj = create<ID>(CB);
const iobj = create(CB);//根据类型推断,可以简写

类型别名

  • 关键字:type
  • type 和 interface 的区别
    • interface 相当于自定义创建了新的类型;type是给已有的类型其别名
    • 可以定义两个同名的interface编译器会自动合并其属性;而type不允许定义重名的类型
    • type可以描述基本数据类型;而interface主要用于引用类型(如:对象)的描述
    • interface可以通过extends继承;而type没有继承,但可以定义联合类型和扩展类型
//类型别名不能自己赋给自己,会造成循环引用的问题
// type Foo = Foo//报错
//在3.6版本后支持定义Tree类型、递归类型
type Tree<T> = {
    value: T,
    next?: Tree<T>//这里必须是可选属性,否则就变成了无限递归
}
const obj:Tree<number> = {
    value: 1,
    next: {
        value: 2
    }
}
//联合类型
type Ta = string | number
//扩展类型
type Tb = { name:string }
type Tc = { age:number }
type Td = Tb & Tc

高级用法

  • 关键字 keyof 获取指定类型中所有的属性,并推断出联合类型
    • keyof只能修饰类型,不能修饰具体的变量/对象
//联合类型
type T1 = string | number
type T2 = 'a' | 'b' | 'c'//one of
type T3 = 'b' | 'c' | 'd'
type T4 = T2 | T3//a b c d

//keyof
interface I1 = { a: number, b: number }
type T5 = keyof I1;//相当于 type T5 = 'a' | 'b'
const obj = {name:'a',age:12}
type T6 = keyof typeof obj;//name age
function getValue<U, UK extends keyof U>(obj: U, key: UK){
    return obj[key]
}
function getValue<U, UK extends keyof U>(obj: U, keys: Array<UK>): U[UK][]{
    return keys.map(k => obj[k]);
}
  • 关键字 in 把一个已知属性绑定到新创建的类型中
type T1 = 'a' | 'b'
type T2 = {
    [key in T1]: string
}

//将一个类型中的属性转换成只读属性
type Tobj = { name:string, age:number, sex?:number }
type TreadonlyObj<T> = {
    readonly [key in keyof T]: T[key]
}
type Tobj2 = TreadonlyObj<Tobj>;//Tobj2中的属性就全变成了只读属性
//把可选属性变成必选属性
type Trequired<T> = {
    [key in keyof T]-?: T[key]//-? 表示去掉?,变成必选属性
}
//筛选出指定的属性
type Tpick<T, K extends keyof T> = {
    [p in K]: T[p]
}
type Tobj3 = Tpick<Tobj, 'name'|'age'>;//type Tobj3 = {name:string, age:number}
  • 条件类型
    • 关键字 extends 判断是否相等
type E<A, B> = A extends B ? A : B;

type TisArray<T> = T extends any[] ? true : false;

type TypeName<T> = T extends string ? 'string' :
    T extends number ? 'number' :
    T extends boolean ? 'boolean' :
    T extends undefined ? 'undefined' :
    T extends Function ? 'Function' :
    'object';
//分布式条件类型 - 剔除掉不需要的类型
type Tdiff<T, K> = T extends K ? never : T;
type Td = Tdiff<'a'|'b':'c', 'a'|'d'>;
//Tdiff<'a', 'a'|'d'> Tdiff<'b', 'a'|'d'> Tdiff<'c', 'a'|'d'>
//never | 'b' | 'c'
type TdiffNullUndefined<T> = Tdiff<T, null|undefined>;
type T1 = TdiffNullUndefined<string|number|null|undefined>;//string|number
  • infer
    • 可以通过推断代码的类型信息,为开发者提供更加智能的代码提示和自动补全功能
    • 内置工具 ReturnType 中就使用了infer
//自己实现ReturnType
type TmyReturnType<T extends Function> = T extends (...args: any) => infer R ? R : never;
  • 类型模板字符串
    • 可以做一些类似正则表达式的匹配
type Tinfo<T extends string, K extends number> = `${T} - ${K}`;
const info:Tinfor<'aa', 10> = 'aa - 10';

type Tuid = `${string}-${string}-${string}-${string}-${string}`;
function fn(uid:Tuid){}
fn('aa-bb-cc-dd-ee');

//示例:限制参数必须是URL格式的
type TURL<P extends string, D extends string> = `${P}://${D}`;
type TvaildUrl<UL extends string> = UL extends TURL<'http'|'https', `${string}.${string}`> ? UL : never;
function testFn<T extends string>(url: TvaildUrl<T>){}
testFn('http://xxx.com')
testFn('https://xxx.com')
// testFn('ftp://xxx.com')//报错

ts内置的泛型工具

  • Partial
    • 将一个对象类型中的所有属性变为可选属性
  • Required
    • 转必选属性
  • Readonly
    • 转只读属性
  • Pick<T,K extends keyof T>
    • 筛选出指定的属性
  • Record<K extends keyof any, T>
    • 定义一个类型,所有属性的类型都为T
  • Exclude<T,U>
    • 从T中去掉所有能够赋值给U的属性,然后剩下的属性构成一个新的类型
  • Extract<T,U>
    • 提取T中所有能够赋值给U的属性,将这些属性构成一个新的类型
  • Omit<T, Ks extends keyof any>
    • 从T中选取所有的属性值,然后移除属性名在Ks中的属性值
    • 本质上是Pick的反向操作,排除掉Ks
  • ReturnType
    • 获取函数返回值的类型
// 获取构造函数函数类型的返回类型
class ClassA {}
type ta = InstanceType<typeof ClassA>

// 自定义泛型工具-定义变量的取值 只能是对象中某个属性的值
const obj = {
  a: 'aa', b: 'bb'
} as const;//必须const 类型断言,指定对象为静态对象,
// type TvalueType = typeof obj[ keyof typeof obj ]
type Tvalues<T> = T[ keyof T ]
const name:Tvalues<typeof obj> = 'aa'//这里的name只能取obj的属性值,即aa或bb

装饰器

  • 使用装饰器后,在编译时需要指定target,否则可能不生效
    • tsc xxx --target es5 --experimentalDecorators --emitDecoratorMetadata
  • 类装饰器
    • 为现有的类添加属性和方法
// function decorators(value:Function, context:ClassDecoratorContext){
//     // value 就是被修饰的类
//     value.prototype.toString = function(){//在原型上添加成员
//         console.log(this.name, this.age)//装饰器中可以访问类的this
//     }
// }
type DcConstructer = { new(...args, any[]):any }
function decorators<T extends DcConstructer>(value:T, context:ClassDecoratorContext){
    return class extends value {//返回一个匿名类
        toString(){
            console.log(this.name, this.age)
        }
    }
}

@decorators
class Test {
    name: 'aa',
    age: 18
}

//第二种写法
type DcConstructer2 = { new(...args, any[]):any }
function decorators2(){
    return function<T extends DcConstructer>(value:T){
        return class extends value {
            sex: 1
        }
    }
}
const TestClass2 = decorators2()(
    class {
        name: 'aa',
        age: 18
    }
)
const tcobj = new TestClass2()
console.log(tcobj.sex)
  • 方法装饰器
    • 拦截函数,对函数进行修改和扩展
function inject(originalMethod:Function, context:ClassMethodDecoratorContext){
    return function(this:any, ...args:any[]){
        console.log('before')
        originalMethod.apply(this, args)
        console.log('after')
    }
}
function inject2(originalMethod:Function, context:ClassMethodDecoratorContext){
    return function(this:any, ...args:any[]){
        console.log('before-2')
        originalMethod.apply(this, args)
        console.log('after-2')
    }
}
class Test {
    @inject
    @inject2
    testFn(){
        console.log('test-fn')
    }
}
new Test().testFn()//before before-2 test-fn after-2 after

//带参数的装饰器
function log(message:string){
    return function (originalMethod:Function, context:ClassMethodDecoratorContext){
        return function(this:any, ...args:any[]){
            console.log(message)
            originalMethod.apply(this, args)
        }
    }
}
class Test2 {
    @log('aa')
    testFn(){}
}

/**
 * 最新实验性语法,有三个参数
 * function fnDecorator(target:Object, propertyKey:string|symbol,descriptor:PropertyDescriptor){}
 */
  • 属性装饰器
    • 要么不返回,要么返回一个函数
function fieldDecorator(value:undefined, context:ClassFieldDecoratorContext){
    return function(initValue:any){
        return initValue + 'haha'
    }
}
class Test {
    @fieldDecorator
    name = 'aa'
}
/**
 * 最新实验性语法
 * function fieldDecorator(target:Object, key:string ){}
 */
  • 访问装饰器
    • 返回一个函数,取代原来的取值
function lazy(value:Function, context:ClassGetterDecoratorContext){
    if(context.kind === 'getter'){
        return function(){
            const res = value.call(this);
            Object.defineProperty(this, context.name, {
                value: res,
                writable: false
            })
            return res;
        }
    }
}
class Test {
    @lazy
    get value(){
        return 'aa'
    }
}

/**
 * 最新实验性语法
 * function fieldDecorator(target:Object, name:string|symbol,descriptor:PropertyDescriptor){}
 */
  • 存取器装饰器
    • 加上accessor关系子修饰的属性,向当于声明类getter和setter
function logged(value, context:ClassAccessorDecoratorContext){
    if(context.kind === 'accessor'){
        const { get, set } = value
        return {
            get(){
                return get.call(this)
            }
            set(val){
                console.log(val)
                set.call(this, val)
            }
            init(initValue){
                return initValue
            }
        }
    }
}
class Test {
    accessor name = 'aa';
    // name = 'aa'
    // get name(){
    //     return this.name
    // }
    // set name(value){
    //     this.name = value
    // }
}

自定义类型声明文件

  • xxx.d.ts
declare let count: number;
interface Point {
    x: number;
    y: number;
}
declare let position: Point;
declare funcition add(x:number, y:number):number
type FomartPoint = (point: Point)=>void
declare const fomartPoint: FonmarePoint;
// 需要导出声明好的类型,才能在其他的 .ts 文件中使用
export {count, position, add, fomartPoint}

//声明文件或模块的语法格式如下:
declare module Module_Name {
}
declare module 'xxx' {
interface RouteMeta{
  keepalive?: boolean;
  title: string;
 }
}
export{};//告诉ts这是一个模块,否则可能不起作用

// global.d.ts 声明全局global格式如下:
export{};
declare global {
interface Window{
    dd: any;
  }
}

tsconfig.json配置规则

{
    /* 根选项 */
    "include": ["./src/**/*"], // 指定被编译文件所在的目录
    "exclude": [], // 指定不需要被编译的目录
    //使用小技巧:在填写路径时 ** 表示任意目录, * 表示任意文件。

    /* 项目选项 */
    "compilerOptions": {
        "target": "ES6", // 目标语言的版本
        "module": "commonjs", // 生成代码的模板标准
        "lib": ["DOM","ES5","ES6","ES7","ScriptHost"], // TS需要引用的库
        "outDir": "./dist", // 指定输出目录
        "rootDir": "./", // 指定输出文件目录(用于输出),用于控制输出目录结构
        "allowJs": true, // 允许编译器编译JS,JSX文件
        "checkJs": true, // 允许在JS文件中报错,通常与allowJS一起使用
        "removeComments": true, // 删除注释
        "esModuleInterop": true, // 允许export=导出,由import from 导入

        /* 严格检查选项 */
        "strict": true, // 开启所有严格的类型检查
        "alwaysStrict": true, // 在代码中注入'use strict'
        "noImplicitAny": true, // 不允许隐式的any类型
        "noImplicitThis": true, // 不允许this有隐式的any类型
        "strictNullChecks": true, // 不允许把null、undefined赋值给其他类型的变量
        "strictBindCallApply": true, // 严格的bind/call/apply检查
        "strictFunctionTypes": true, // 不允许函数参数双向协变
        "strictPropertyInitialization": true, // 类的实例属性必须初始化

        /* 额外检查 */
        "noUnusedLocals": true,//是否检查未使用的局部变量
        "noUnusedParameters": true,//是否检查未使用的参数
        "noImplicitReturns": true,//检查函数是否不含有隐式返回值
        "noImplicitOverride": true,//是否检查子类继承自基类时,其重载的函数命名与基类的函数不同步问题
        "noFallthroughCasesInSwitch": true,//检查switch中是否含有case没有使用break跳出
        "noUncheckedIndexedAccess": true,//是否通过索引签名来描述对象上有未知键但已知值的对象
        "noPropertyAccessFromIndexSignature": true,//是否通过" . “(obj.key) 语法访问字段和"索引”( obj[“key”]), 以及在类型中声明属性的方式之间的一致性

        /* 实验选项 */
        "experimentalDecorators": true,//是否启用对装饰器的实验性支持,装饰器是一种语言特性,还没有完全被 JavaScript 规范批准
        "emitDecoratorMetadata": true,//为装饰器启用对发出类型元数据的实验性支持

        /* 高级选项 */
        "forceConsistentCasingInFileNames": true,//是否区分文件系统大小写规则
        "extendedDiagnostics": false,//是否查看 TS 在编译时花费的时间
        "noEmitOnError": true,//有错误时不进行编译
        "resolveJsonModule": true,//是否解析 JSON 模块
    },
}

推荐文章