typeScript小记

77 阅读6分钟

强类型和弱类型

  • js属于弱类型语言
  • ts将js这种弱类型语言变成强类型语言

静态类型和动态类型

  • 静态类型语言:在编译阶段确定所有变量类型
  • 动态类型语言:在执行阶段确定所有变量类型

ts入门

  • 1.全局安装ts:npm i typescript -g(可使用tsc)
  • 2.ts配置文件初始化:tsc --init 生成tsconfig.json
  • 3.编译ts文件:tsc ./src/index
  • 4.添加webpack打包工具
  • 5.添加ts-loader

ts基本类型

  • 原始类型
let bool: boolean = true
let num: number | undefined | null = 123
let str: string = 'abc'
// str = 123

// 数组
let arr1: number[] = [1, 2, 3]
let arr2: Array<number | string> = [1, 2, 3, '4']

// 元组
let tuple: [number, string] = [0, '1']
// tuple.push(2) 可以添加新元素
// console.log(tuple) 
// tuple[2] 不能访问到新元素

// 函数
let add = (x: number, y: number) => x + y //返回值类型可以不写,ts有类型推断
let compute: (x: number, y: number) => number
compute = (a, b) => a + b

// 对象
let obj: { x: number, y: number } = { x: 1, y: 2 }
obj.x = 3

// symbol
let s1: symbol = Symbol()
let s2 = Symbol()
// console.log(s1 === s2) false

// undefined, null
let un: undefined = undefined
let nu: null = null
num = undefined //定义num时候就要允许类型为undefined
num = null

// void 没有任何返回值
let noReturn = () => {}

// 如果不指定类型,默认any类型,和js没有区别
let x
x = 1
x = []
x = () => {}

// never
let error = () => {
    throw new Error('error')
}
let endless = () => {
    while(true) {}
}
  • 枚举类型

一组有名字的常量集合,通常用于定义一些常量,便于代码阅读,枚举分为数字枚举和字符串枚举

  • 如下代码,可读性非常差,可维护性也差 可以使用枚举来解决
enum Role {
    Reporter = 1, //自定义初始值
    Developer,
    Maintainer,
    Owner,
    Guest
}
// 数字枚举,值会递增
enum Role {
    Reporter = 1, //自定义初始值
    Developer,
    Maintainer,
    Owner,
    Guest
}
// console.log(Role.Reporter)
// console.log(Role)

// 字符串枚举
enum Message {
    Success = '恭喜你,成功了',
    Fail = '抱歉,失败了'
}

// 异构枚举,容易混淆,不建议使用
enum Answer {
    N,
    Y = 'Yes'
}

// 枚举成员,只读,不能修改
// Role.Reporter = 0

enum Char {
    // const member
    a,
    b = Char.a,
    c = 1 + 3,
    // computed member
    d = Math.random(),
    e = '123'.length,
    f = 4
}

// 常量枚举 用const声明,在编译阶段会被移除
const enum Month {
    Jan,
    Feb,
    Mar,
    Apr = Month.Mar + 1,
    // May = () => 5
}

let month = [Month.Jan, Month.Feb, Month.Mar]

// 枚举类型
enum E { a, b }
enum F { a = 0, b = 1 }
enum G { a = 'apple', b = 'banana' }

let e: E = 3
let f: F = 3
// console.log(e === f) 不能进行比较

let e1: E.a = 3
let e2: E.b = 3
let e3: E.a = 3
// console.log(e1 === e2)
// console.log(e1 === e3) //只有自己可以和自己比较

let g1: G = G.a
let g2: G.a = G.a

ts对象类型接口

interface StringArray {
    [index: number]: string
}
let chars: StringArray = ['a', 'b']
type Add = (x: number, y: number) => number
let add: Add = (a: number, b: number) => a + b

ts函数类型接口

  • 函数定义固定参数
// 函数定义
function add1(x: number, y: number) {
    return x + y
}
let add2: (x: number, y: number) => number

type add3 = (x: number, y: number) => number

interface add4 {
    (x: number, y: number): number
}
  • 可选参数
function add5(x: number, y?: number) {
    return y ? x + y : x
}
add5(1)
  • 函数重载
function add8(...rest: number[]): number;
function add8(...rest: string[]): string;
function add8(...rest: any[]) {
    let first = rest[0];
    if (typeof first === 'number') {
        return rest.reduce((pre, cur) => pre + cur);
    }
    if (typeof first === 'string') {
        return rest.join('');
    }
}
console.log(add8(1, 2))
console.log(add8('a', 'b', 'c'))

ts函数类型接口

  • 类的继承和成员修饰符,抽象类
abstract class Animal { //抽象类
    eat() {
        console.log('eat')
    }
    abstract sleep(): void //抽象方法
}
// let animal = new Animal() //抽象类不能被实力化

class Dog extends Animal { // 抽象类可以被继承
    constructor(name: string) {
        super()
        this.name = name
        this.pri()
    }
    public name: string = 'dog'
    run() {}
    private pri() {} //私有方法,只能自己调用
    protected pro() {} //保护方法只能自己和子类使用
    readonly legs: number = 4 //只读
    static food: string = 'bones' //静态成员,只能通过类名,不能通过子类调用,但是可以被继承
    sleep() {
        console.log('Dog sleep')
    }
}
// console.log(Dog.prototype)
let dog = new Dog('wangwang')
// console.log(dog)
// dog.pri()
// dog.pro()
console.log(Dog.food) //只能通过类名
//console.log(dog.food)//报错,
dog.eat()

class Husky extends Dog {
    //实例属性
    constructor(name: string, public color: string) {
        super(name)
        this.color = color
        // this.pri()
        this.pro()
    }
    // color: string
}
console.log(Husky.food) //继承后可以调用
  • 抽象方法的多态,继上一个
class Cat extends Animal {
    sleep() {
        console.log('Cat sleep')
    }
}
let cat = new Cat()

let animals: Animal[] = [dog, cat]
animals.forEach(i => {
    i.sleep()
})

ts泛型

function log<T>(value: T): T {
    console.log(value);
    return value;
}
log<string[]>(['a', ',b', 'c'])
log(['a', ',b', 'c'])

// type Log = <T>(value: T) => T
// let myLog: Log = log

// interface Log<T> {
//     (value: T): T
// }
// let myLog: Log<number> = log
// myLog(1)

class Log<T> {
    run(value: T) {
        console.log(value)
        return value
    }
}
let log1 = new Log<number>()
log1.run(1)
let log2 = new Log()
log2.run({ a: 1 })

interface Length {
    length: number
}
function logAdvance<T extends Length>(value: T): T {
    console.log(value, value.length);
    return value;
}
logAdvance([1])
logAdvance('123')
logAdvance({ length: 3 })

1.函数和类可以轻松地支持多种类型,增强程序的课拓展性 2.不必写多条函数重载,冗长的联合类型声明,增强代码可读性 3.灵活控制类型之间的约束

ts兼容性

x兼容Y:x(目标类型)=Y(源类型) 口诀: 结构之间兼容:成员少的兼容成员多的 函数之间兼容:参数多的兼容参数少的

ts类型保护机制

//instanceof
 if (lang instanceof Java) {
         lang.helloJava()
         // lang.helloJavaScript()
     } else {
         lang.helloJavaScript()
     }

    // in
     if ('java' in lang) {
         lang.helloJava()
     } else {
         lang.helloJavaScript()
     }

    // typeof
     if (typeof x === 'string') {
         console.log(x.length)
     } else {
         console.log(x.toFixed(2))
     }

ts高级类型

  • 交叉类型和联合类型
interface DogInterface {
    run(): void
}
interface CatInterface {
    jump(): void
}
let pet: DogInterface & CatInterface = {
    run() {},
    jump() {}
}

class Dog implements DogInterface {
    run() {}
    eat() {}
}
class Cat  implements CatInterface {
    jump() {}
    eat() {}
}
enum Master { Boy, Girl }
function getPet(master: Master) {
    let pet = master === Master.Boy ? new Dog() : new Cat();
    // pet.run()
    //pet.jump()
    pet.eat()
    return pet
}

interface Square {
    kind: "square";
    size: number;
}
interface Rectangle {
    kind: "rectangle";
    width: number;
    height: number;
}
interface Circle {
    kind: "circle";
    radius: number;
}
type Shape = Square | Rectangle | Circle
function area(s: Shape) {
    switch (s.kind) {
        case "square":
            return s.size * s.size;
        case "rectangle":
            return s.height * s.width;
        case 'circle':
            return Math.PI * s.radius ** 2
        default:
            return ((e: never) => {throw new Error(e)})(s)
    }
}
console.log(area({kind: 'circle', radius: 1}))
  • 索引类型
let obj = {
    a: 1,
    b: 2,
    c: 3
}

// function getValues(obj: any, keys: string[]) {
//     return keys.map(key => obj[key])
// }
function getValues<T, K extends keyof T>(obj: T, keys: K[]): T[K][] {
    return keys.map(key => obj[key])
}
console.log(getValues(obj, ['a', 'b']))
// console.log(getValues(obj, ['d', 'e']))

// keyof T 约定key的联合类型
interface Obj {
    a: number;
    b: string;
}
let key: keyof Obj

// T[K] //约定取值
let value: Obj['a']

// T extends U
  • 映射类型
interface Obj {
    a: string;
    b: number;
    c: boolean;
}
type ReadonlyObj = Readonly<Obj> //只能映射

type PartialObj = Partial<Obj> //可选映射

type PickObj = Pick<Obj, 'a' | 'b'> //抽取映射 

type RecordObj = Record<'x' | 'y', Obj> //非同态映射
  • 条件类型
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 T1 = TypeName<string>
type T2 = TypeName<string[]>

// (A | B) extends U ? X : Y
// (A extends U ? X : Y) | (B extends U ? X : Y)
type T3 = TypeName<string | string[]>

type Diff<T, U> = T extends U ? never : T
type T4 = Diff<"a" | "b" | "c", "a" | "e">
// Diff<"a", "a" | "e"> | Diff<"b", "a" | "e"> | Diff<"c", "a" | "e">
// never | "b" | "c"
// "b" | "c"

type NotNull<T> = Diff<T, null | undefined>
type T5 = NotNull<string | number | undefined | null>

// Exclude<T, U>
// NonNullable<T>

// Extract<T, U>
type T6 = Extract<"a" | "b" | "c", "a" | "e">

// ReturnType<T>
type T8 = ReturnType<() => string>

ts的命名空间

namespace Shape {
    export function square(x: number) {
        return x * x
    }
}
console.log(Shape.square(2))

ts的声明合并

  • 接口合并
interface A {
    x: number;
    // y: string;
    foo(bar: number): number; // 5
    foo(bar: 'a'): string; // 2
}

interface A {
    y: number;
    foo(bar: string): string; // 3
    foo(bar: string[]): string[]; // 4
    foo(bar: 'b'): string; // 1
}

let a: A = { //具备两个接口所有的参数
    x: 1,
    y: 2,
    foo(bar: any) {
        return bar
    }
}
  • 命名空间合并
class C {}
namespace C { //命名空间的声明必须在类之后
    export let state = 1
}
console.log(C.state

ts中如何编写声明文件

  • 安装第三方类库的时候,我们需要对其进行定义,比如jquery这个库,这个时候我们可以使用社区提供的
npm i @types/jquery -D
  • 如果社区没有提供,我们可以怎么定义呢?
//js 
function globalLib(options) {
    console.log(options);
}

globalLib.version = '1.0.0';

globalLib.doSomething = function() {
    console.log('globalLib do something');
};
//d.ts文件
declare function globalLib(options: globalLib.Options): void;

declare namespace globalLib {
    const version: string;
    function doSomething(): void;
    interface Options {
        [key: string]: any
    }
}