本篇文章只是为了记录易漏知识点,并不过多过全的说 typescript —–只针对个人

为什么要学习 TypeScript

  • 1.静态编译,与 JavaScript 不同的是,Typescript 可以在编译时检测,限制类型,更加严格
  • 2.可以使用提案中的语法特性,更重要的是静态语言
  • 3.Typescript 是 JavaScript 的语法糖
  • 4.将会是前端未来的主力军
  • 5.利于团队协作,严谨不是灵活
  • 6.智能的类型推断

类型注意事项

  • 1.any 是项目开发的大忌,除非在不得已的情况下,否则不建议会用 any,找不到类型时,建议去对应的库下看看 index.d.ts
  • 2.any 与 unknown 的区别,unknown 不能实际操作(比如调用方法,自身属性等),只能赋值,而 any 既可以赋值,也可以操作
  • 3.nerver,表示永远不存在的值,是任何类型的子类型,但是任何值不可以赋值给 never 类型(除 never 类型外)
  • 4.元组:元组表示一个已知数量和类型的数组,个元素的类型可以不同
const arr:Array<number>=[1,2,3,4,5];
const arr2:number[]=[1,2,3,4,5];
// 元组
const arr2:[string,number]=['duanxl',26]
// 元组定义
interface Tuple extends Array<string | number> {
    0:string;
    1:number;
    length:2;
}
// 元组越界问题
const tuple:[string, number]=['duan',26];
tuple.push(99);
console.log(tuple) // ['duan',26,99]
console.log(tuple[2])  // Tuple type '[string, number]' of length '2' has no element at index '2'. ts(2493)
// 静态类型的原因
  • 5.枚举定义不赋值,默认从 0 开始依次累加,key 及 value 可以相互寻找
// 枚举编译后
var Direction;
(function(){
    Direction[Direction['top']=10]='top';
    Direction[Direction['bottom']=20]='bottom';
    Direction[Direction['left']=30]='left';
    Direction[Direction['right']=40]='right';
}(Direction || Direction={}))
// example
Direction.top // 10
Direction[Direction.top] // 'top'
  • 6.联合枚举
enum Direction {
    Left,
    Right,
    Top,
    Bottom
}
enum Food{
    Hotdog,
    Meat
}
let a:Direction;

a=Direction.Left;
a=Food.Hotdog;  //Type 'Food.Hotdog' is not assignable to type 'Direction'.ts(2322)
// 值虽然一样,但是类型不一样

Interface 接口

  • 为什么要用接口

    接口主要是对值所具具有的解构进行类型检查,也可以说是值的定义规则

  • 接口和类有什么区别

    类主要是通过继承,接口是组合,组合大于继承,在设计模式里,组合更加易于维护,但是如果你想初始化一个带有默认值的复杂类型,建议使用类作为接口

访问限定符

  • 1.public 在 Typescript 中,成员默认未 public,可以被自己及外部访问
  • 2.private 只可以被类内部访问
  • 3.protected 可以被类内部访问以及子类访问

泛型约束

由于很少使用泛型 extends、keyof 等关键字,再次回顾

  • extends 相对于泛型,extends 是对泛型类型的约束
  • keyof 约束泛型类型属于其中的一个属性
function getValue<T extends object,U extends keyof T>(obj:T,key:U){
    return obj[key]
}
  • 与 new 的使用
function makeType<T>(type:{new() : T}):T{
    return new type()
}

简单类型的使用

交叉类型是将多个类型合并成一个类型

  • 联合类型,使用”|”作为标记
function formatCommandLine(command:string[]|string){
    let line='';
    if(typeof command === 'string'){
        line=command.trim()
    }else{
        line=command.join(' ').trim();
    }
}
  • 交叉类型,使用”&”作为标记
    function mixin<T entends object,U extends object>(first:T,second:U):T & U{
        const result=<T & U>{};
        for(let id in first){
            (<T>first)[id]=first[id]
        }
        for(let id in second){
            if(!result.hasOwnProperty(id)){
                (<U>result)[id]=second[id]
            }
        }
        return result
    }
  • 类型别名
type some=boolean | string;
  • 字面量类型
type Direction='Month' | 'Year' | 'Day';
const hello:'hello'='world' // error
  • 类型字面量

    与 JavaScript 中的对象字面量相似

type Foo {
    baz:[
        number,
        'duanxl'
    ],
    toString():string;
    readonly [Symbol.iterator]: "duanxl's blog";
}
  • 可识别联合类型(一个类型实现兼容两个类型)
type usserAction {
    id: number;
    action: 'delete';
    info: Info
} |
{
    action: 'create';
    info: Info
}
  • 类型断言

    我们知道断言可以使用 User,也可以使用”as”

  • 双重断言

interface Person{
    name:string;
    age:number;
}
const person='duanxl' as Person; // error
const person = 'duanxl' as any as Person;  // success
// 一般用于欺骗编译器。。。
  • 类型守卫

    主要用于缩减类型范围

    • 1.instanceof 是否符合类型
    interface Person{
        name:string;
        age:number;
    }
    interface Animal{
        name:string;
        color:string;
    }
    function getInfo(arg: Person | Animal){
        if(arg instanceof Person){
            console.log(arg.color) // error
            console.log(arg.age) // success
        }
    }
    
    • 2.in 是否存在类型中
    function getInfo(arg: Person | Animal){
        if('age' in arg){
            console.log(arg.color) // error
            console.log(arg.age) // success
        }
    }
    

类型兼容性

如果 x 要兼容 y,那么 y 至少要具有 x 相同的属性。即短的兼容长的,反过来则不行

class Person {
    constructor(public weight: number, public name: string, public edu: string){}
}
interface Dog {
    name:string;
    weight:number;
}
let x:Dog;
x = new Person(120,'duanxl','bk') // success

函数兼容性

与上面的类型兼容性相反,如果 x 要兼容 y,那么 y 的参数及类型包含 x 的参数类型

let fn1=(a: number)=>0;
let fn2=(b:number,c:string)=>0;
fn2=fn1 // success
fn1=fn2 // Error

类的兼容性

与类型兼容性相似,只是类的兼容性仅比较实例成员和方法,不会比较构造函数和静态成员

class Person{
    name: string;
    constructor(name:string,age:number){
        this.name=name;
    }
}
class Animal{
    name:string;
    constructor(name:string){
        this.name=name;
    }
}
let p:Person= new Person('duanxl',26);
let a:Animal=new Animal('pig');
p = a // success
a = p // success

is 关键字

与上面的类型守卫相似,也用于缩短范围

function isString(test:any):test is string{
    return typeof test==='string';
}
function example(foo: number | string){
    if(isString(foo)){
        console.log(foo.length) // success
    }else{
        console.log(foo)
    }
}

类型注解

interface Func {
  (): string;
  new (): string;
}
declare const toAnyString: Func;
toAnyString();
new toAnyString();

高级类型

  • 索引类型 keyof 上面也有说到
    /**
    * 简单实现一个从对象里得到属性值的方法
    */

    const user = {
      user: "duan",
      age: 26,
      id: "xxxx",
      role: "vip",
    };
    // 简陋版
    interface Obj {
      [key: string]: any;
    }
    function getValue(o: Obj, names: string[]) {
      return names.map((item) => o[item]);
    }
    // keyof 版
    function getValue2<T, U extends keyof T>(o: T, names: U[]): T[U][] {
      return names.map((item) => o[item]);
    }
    getValue(user, ["aa", "bb"]); // 无静态限制
    getValue2(user, ["user", "role"]); // 有静态限制

映射类型 [Key in Keys]

假如我们要将一个类型进行扩展,但是不需要类型中的所有属性,那么我们可以把类型变成可选的,可以理解为 for … in 循环

type Person<T> = { [K in keyof T] ?: T[K] }

条件类型

用法和我们的三元运算差不多,关键字:extends
最简单的类型编程。。。

declear function fn<T extends boolean>(x : T):T extends true ? string : number;
const fn1 = fn(Math.random() < 0.5);
const fn2 = fn(false) // number
const fn3 = fn(true) // string
// 很遗憾,技术还没到写架构接口这块。。。

裸参数类型

只考虑 T 存在一种类型

// 没有任何参数被数组包裹
type TypeOne<T> = T extends boolean ? 'yes' : 'no';

元组条件类型

考虑 T 存在可选类型

// 类型参数被数组包裹
type TypeTwo<T> = [T] extends [boolean] ? 'yes' : 'no'

联合类型 + 条件类型

如果存在多个类型参数,且都是联合类型

type Diff<T, U> = T extends U ? never : T;
type R = Diff<"a" | "b" | "c", "d" | "b" | "f">;  // "a" | "c"


// example2:
type Filter<T , U> = T extends U ? Y :never;
type R1 = Filter<string | boolean | (()=>void) , Function>; // ()=>void

条件类型 + 映射类型

假如我们把泛型想象成对象字面量,那么我们如何将 interface 函数类型的名称取出来

interface Part {
  id: string;
  name: string;
  subParts: Part[];
  updatePart(newVal: string): void;
}
interface Part2 {
  id: string;
  name: string;
  subParts: Part[];
  updateParts(x: Part): void;
}

type GetFunctionTypes<T> = {
  [K in keyof T]: T[K] extends Function ? K : never;
}[keyof T];

type Res=GetFunctionTypes<Part> // "updatePart"

infer 关键字

可以理解成摘取成中间类型

/**
 * 我们现在实现一个字面量类型转换
 * 即 [string,number] --> string | number
 */
type Type1 = [number, string];
type ElementOf<T> = T extends Array<infer T> ? T : never;
type Res = ElementOf<Type1>; // string | number

加深影响,下面的 infer,我们可以继续想象成摘取
对应类型,则 infer P 即代表摘取到的返回类型

interface User {
  id: string;
  name: string;
  form?: string;
}
type Foo = () => User;
type Return<T> = T extends (...args: any[]) => infer P ? P : any;
type Res = Return<Foo>;  // User

typeof 关键字

typeof,类型保护
typeof,对于值,与 JavaScript 一样,返回字符串的类型值,对于 class,可以理解成将 class 的类型映射出来

class Person{
    constructor(name:string,age:number){}
}
type Type=typeof Person;
// new(name:String,age:number)=>any:

再次加深,typeof 与 infer 的使用

class Person {
  constructor(name: string, age: number) {}
}
type Type = typeof Person;

type ConstructorParamers<
  T extends new (...args: any[]) => any
> = T extends new (...args: infer P) => any ? P : never;

type Res = ConstructorParamers<Type>; // [name:string,age:number]

之前漏掉的修饰符

  • -? 非
  • -readonly 非只读

常见工具类

  • Omit 忽略类型参数
type Foo=Omit<{name:String,age:number},'name'> // {age:number}
  • Merge 合并类型
  • Partial 上面我们实现过,将类型参数全部变为可选
  • Required 将类型参数全部变为必选
  • Record 将 K 中所有属性装换为 T 类型
type Record<K extends keyof any, T> = {
    [P in K]: T;
};
  • Pick 上面也实现过,将某个类型中的子属性挑出来,变成包含这个类型部分属性的子类型,即并集
type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
};
  • Exclude 将某个类型中属于另一个的类型移除掉
type Exclude<T, U> = T extends U ? never : T;
  • Extract 从 T 中提取 U

编译器的处理

  • 1.扫描器扫描生成 token 流
  • 2.将 token 流抽象成 AST 语法树
  • 3.绑定器将 AST 中的声明节点与相同实体形成符号(Symbols)
  • 4.检查器通过符号验证代码语义
  • 5.通过发射器生成 JavaScript 代码
0