typescript


简单类型

基本类型

  • number
  • string
  • boolean
  • never
  • void
  • object
  • symbol
  • bigint

let num: number = 1;
let str: string = '1';
let bol: boolean = true;
let s1: symbol = Symbol(1);
let max = Number.MAX_SAFE_INTEGER
let r1:bigint = BigInt(max);
// never 永远打不到的类型
function throwError(): never {
    throw new Error()
}
function setVal(val: string) {
    if (typeof val === 'string')
    else val // never
}
function whileTrue(): never {
    while (true) { }
}

// void 一般用于函数无返回值
function getVoid():void {...}

联合类型

多个类型的集合


// 类型联合
let numOrStr: number | string = '1';
numOrStr = 1;
// 固定值联合
type IType = 'a' | 'b' | 'c' | 'd'
let type: IType = 'a';
type = 'b';

函数类型

函数可以定义参数类型和返回值类型, 也可以给函数本身添加类型,两方添一方即可,会自动推导类型


// 自动推导sum的类型
function sum(x: number, y: string): string { // 函数 括号后面的是返回值类型
    return x + y;
}
// 自动推导参数和返回值类型
type IFn = (a: number, b: number) => number
const sum1: IFn = (x,y) => x + y;
// 多余参数用扩展运算符
const sum3 = (x: number, y?: number, ...args: number[]): number => {
    return x + (y as number);
}
// 函数重载 重载方法在真实方法的上面
function toArray(value:string):string[]
function toArray(value:number):number[]
function toArray(value:string | number) {
    ......
}

类的类型

类的修饰符

  • public 是属性修饰符 public 表示自己 和 子类 和子类之外都可以访问到
  • protected 只有自己和 自己的后辈能访问
  • private 就是只有自己能访问的属性

类中的装饰器: 修饰类会将target 目标类传给定义的函数 修饰属性会将目标类和属性key传给定义好的函数 修饰方法 。。。 修饰参数 。。。


@addSaay
class Person {
    // eat!:()=>void
    @toUpperCase
    public name: string = 'jw';
    @double(3)
    static age: number = 18; // 静态的通过类来调用
    @Enum(false)
    // 还能修饰参数
    drink(@params content: any) { }
}
function double(num: number) {
    return function (target: any, key: string) { // target => 类
        let v = target[key]
        Object.defineProperty(target, key, {
            get() {
                return num * v
            }
        })
    }
}

接口

使用interface声明一个接口,用来描述对象的结构 也可以使用type来定义接口

  • 区别,interface可以被类实现和继承,type可以使用联合类型
  • 多个重名接口会合并

// kv之间可以用逗号分隔,也可以: 也可以无符号回车
interface IObj {
    (params: string): string // 函数
    readonly read: boolean // 仅读
    name: string // 必填
    age?: number // 选填
    [key: string]: any // 扩展任意值
}
interface My extends IObj {} // 继承

接口实现: 使用implements关键字继承,可继承多个接口,用逗号隔开


interface ISpeakable {
    name: string,
    speak(): void
}
interface IChineseSpeakable {
    speakChinese(): void
}
class Speak implements ISpeakable, IChineseSpeakable {
    speakChinese(): void {
        throw new Error("Method not implemented.");
    }
    name!: string
    speak(): string { // 此方法是原型方法
        return 'xxx'
    }
}

泛型

泛型的用处在于 当我们调用的时候 确定类型,而不是一开始就写好类型,类型不确定,只有在执行的时候才能确定

  • 泛型可以在 函数 类 (接口、别名) 中使用
  • extends 约束
  • keyof 取当前类型的key
  • typeof 取当前值的类型

// 接口使用泛型
interface IMyArr<T> {
    name: T
}
let my: IMyArr<string> = {
    name: 'str'
}
// 函数使用泛型
const swap = <T, K>(tuple: [T, K]): [K, T] => { // 元组是特殊的数组
    return [tuple[1], tuple[0]]
}

类的约束


// 必须包含length属性
type withLen = { length: number }

const computeArrayLength = <T extends withLen, K extends withLen>(arr1: T, arr2: K): number => {
    return arr1.length + arr2.length
}
computeArrayLength('123', { length: 3 })

const getVal = <T extends object, K extends keyof T>(obj: T, key: K) => {
    if (typeof obj !== 'object') {
        return
    }
}

keyof


 keyof { a: 1, b: 2 } // => 'a' | 'b'
 keyof any // => number | string | symbal
 keyof string // => string的属性和方法对应的key

类型保护

js类型判断方法

  • typeof
  • instanceof
  • in语法

// typeof
function fn(val: string | number) {
    if (typeof val == 'string') {
        val.match
    } else {
        val.toFixed
    }
}
// instanceof
class Person { eat() { } }
class Dog { }
const createClass = (clazz: new () => Person | Dog) => {
    return new clazz
}
let r = createClass(Person);
if (r instanceof Person) {
    r.eat // Person
} else {
    r // Dog
}

is语法


// 用于判断方法,断言返回值的类型,不关心方法内容

function isString(val: any): val is string { // 根据函数的返回值确定是不是string类型
    // ts 是给代码的  js是自己的逻辑  ts不关心,ts只关心类型
    return Object.prototype.toString.call(val) == '[object String]'
}

完整性保护


const assert = (obj: never) => { throw new Error("err"); }
// 完整性保护 保证代码逻辑全部覆盖到
function getArea(obj: ISquare | IRant | ICircle) {
    switch (obj.kind) {
        case 'square':
            return obj.width * obj.width;
        case 'rant':
            return obj.width * obj.height;
        case 'circle':
            return
        default:
            assert(obj);
    }
}
getArea({ kind: 'circle', r: 10 });

交叉

交叉类型 = 交集

  • 可用于在原有类型基础上扩展类型
  • 交叉类型可以赋值给没有交叉之前的类型

interface Person1 {
    handsome: string,
    // a:string  如果两个类型不一致 则相交的结果是never
}
interface Person2 {
    height: string,
    // a:number
}

type Person3 = Person1 & Person2; // | 并集  & 交集  (交集可以理解成 涵盖所有属性)

let person: Person3 = {
    handsome: '帅',
    height: '高',
}

内置类型

条件类型

  • Exclude:ts中内置的类型 内置类型包含条件的情况 (内部用条件来实现的)
  • Extract:多个属性中 抽离某几个
  • NonNullable:在多个类型中排除null类型(undefined也会被排除)

type Exclude<T, K> = T extends K ? never : T; // 在多个类型中提排除掉某几个
type MyExclude = Exclude<string | number | boolean, boolean> // string | number

type MyExtract = Extract<string | number | boolean, boolean> // =>  boolean

type NonNullable<T> = T extends null | undefined ? never : T; // 在多个类型中提排除掉某几个
type MyNonNullable = NonNullable<string | number | null | undefined> // string | number

  • ReturnType: 获取返回值类型
  • Parameters: 获取参数类型
  • ConstructorParameters: 获取类的参数类型

// infer放在哪里 就是推断哪里的结果
function getSchool(x: string, y: number) {
    return { name: 'jiatengda', age: 12 }
}
type ReturnType<T extends ((...args: any[]) => any)> = T extends ((...args: any[]) => infer R) ? R : any
type MyReturnType = ReturnType<typeof getSchool>; // {name: string;age: number;}


type Parameters<T extends ((...args: any[]) => any)> = T extends (...args: infer P) => any ? P : any
type MyParameters = Parameters<typeof getSchool>; // [x: string, y: number]

// ------------

class Person {
    constructor(name:string){}
}
type ConstructorParameters<T extends new (...args:any[])=> any> = T extends new (...args:infer CP)=> any ? CP:any
type MyConstructorParameters = ConstructorParameters<typeof Person> // [name: string]

其他内置类型

  • Partial: 表示选项可以是选填的 , 深度递归 ,默认不是深度递归
  • Required -? 去掉可选
  • Readonly 全部只读
  • Pick 精挑细选 (对象里选属性) extract 抽离可用的 (类型中选择类型)
  • Omit 忽略属性

interface ICompany {
    name: string,
    address: string
}
interface IPerson {
    name?: string,
    age: number,
    company?: ICompany
}
// 内置类型默认不递归,在内置类型上扩展
type Partial<T> = { [K in keyof T]?: T[K] extends object?Partial<T[K]>:T[K]}
type MyPerson = Partial<IPerson> 
/*
MyPerson = {
    name?: string | undefined;
    age?: number | undefined;
    company?: ICompany | undefined;
}
*/
type Required<T> = { [K in keyof T]-?: T[K] }
type MyRequired = Required<MyPerson>

type Readonly<T> = { readonly [K in keyof T]: T[K] }
type MyReadonly = Readonly<IPerson>


type Pick<T, K extends keyof T> = { [X in K]: T[X] }; // 挑选属性
type MyPick = Pick<IPerson, 'age' | 'company'>

// 可以忽略再加上 修改新增的属性类型值等
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>
type MyType = Omit<IPerson, 'name'> & { name: string };

根据内置类型 自定义类型

  • 差集 获取两个类型的差集 exclude 在一群类型中忽略掉某个类型 和 omit 对象中忽略
  • 交集
  • 对象合并


let person1 = {
    name: 'jiatengda',
    age: 12,
    address: '回龙观'
}
let person2: {
    address: '回龙观'
}
type Diff<T extends object, K extends object> = Omit<T, keyof K>;
type myDiff = Diff<typeof person1, typeof person2>;
/*
{
    name: string;
    age: number;
}
*/
// 交集 
type Inter<T extends object, K extends object> = Pick<K, Extract<keyof T, keyof K>>
type myInter = Inter<typeof person1, typeof person2>;
/*
{
    address: '回龙观';
}
*/
// 将类型展开
type Compute<T> = { [K in keyof T]: Compute<T[K]> }; // 将类型展开方便提示
//  对象合并
type Merge<T extends object, K extends object> = Omit<T, keyof K> & K;

type myMerge = Compute<Merge<Person1, Person2>>;

命名空间

为了解决命名冲突,称之为文件内的模块,一般开发中都是使用es6module、cjs 进行模块开发
关键字: namespace module,两个关键字都可以声明命名空间

  • 命名空间就是通过自执行函数来是实现的,我们一般写代码不会使用
  • 定义的内容需要export导出
  • 两个重名的命名空间会合并,但是合并后重名的会报错
  • 命名空间可以进行无线嵌套

namespace Home1 {
    //export class Dog{} // 命名空间中的内容 需要导出
    export const b = 'abc'
    export namespace Graden{
        export const a = '花园'; 
    }
}

声明文件

何时使用声明文件?

  • 引入第三方模块时,第三方模块不是用ts写的
  • cdn引入,声明全局变量
  • 给内置类原型扩展属性方法时

一般第三方模块的ts声明文件都会在@types/xxx 的包中维护



declare let a:string;
declare function fn():void;
declare namespace A {
      const a:string // declare中的内容 不需要默认导出,也不用declare了
}

// 声明模块
declare module '*.vue' {
    const component:object;
    export default component
}
declare module '*.jpg'{}

声明命名可能会冲突

  • 接口同名默认会合并
  • 命名空间也能合并
  • 函数和命名空间能合并

文章作者: Jia
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Jia !
  目录