TypeScript查漏补缺笔记

112 阅读6分钟

数组定义方式

const a: number[] = [1]
const a: Array<number> = [1]

undefined和null

undefined和null是所有类型的子集,可以把他们赋值给任何类型。但是在严格模式下不可以: tsconfig.json:

"strictNullChecks":true

严格模式下的特例:

const a:void = undefined

unknown和any

unknown和any都可接受任何类型的赋值,但是any可以赋值给任何类型,unknown只能赋值给unknown和any

可选参数与默认参数

const a = (pramas?: string) => {
    console.log(pramas) // 可能是undefined
}

const b = (params: string = "bbb") => {
    console.log(params) // "bbb"
}

函数重载

在没有弱类型的js语言中,定义同名函数编译是会直接报错的,但是由于ts在定义函数的时候指定了类型,所以可以支持函数重载啦。 函数重载指的是接受同名,但是指定了不同类型入参的函数,返回的结果根据入参的不同而不同。

但是在写函数重载时,需要注意的是在最后一个重载函数书写完毕之前,都不能写具体函数体。

function a(i: number, j: number):number
function a(i: string, j: string): string
function a(i: string, j: number): string {
    return i + j
}

但是这种情况我直接写any或者T它不香吗?(狗头

类型断言

如果在声明变量时用到了any或者unknown, 但是在使用时又明确这个变量的类型,可以使用类型断言: as 或 <>

const a: any = "a,b"
console.log((a as string).split(",")[0]) // a
console.log((<string>a).split(",")[0]) // a

类型交叉

如果实际使用中,需要同时用到两种接口(interface),可以使用&定义交叉类型:

interface a {
    color: string,
    width: number
}

interface b {
    width: number,
    height: number,
}

const a: a&b = {
    color: "red",
    width: 100,
    height: 100
}

上述代码如果两个width定义的类型不同,a&b后会直接给width定义为never。

泛型

泛型的作用是当定义一个类/接口/类型/函数时,确定但不完全确定类型时,可以通过泛型去定义,通常用T代表泛型,但这只是通常,起其他名字也没毛病。

举个例子,当一个函数入参可能是number | string | Array,返回类型与之对应时,可以使用泛型去限制:

    function a<T>(params: T): T => {
        return params
    }
    
    a<string>("i'm string")

可以将泛型理解为另一种函数入参数,只不过这个参数只代表类型。

泛型也可以使用extends关键字继承

当定义泛型时不知道具体类型,但期望其中有一些属性是固定的,可以通过extends实现

    interface IA {
        age: number
    }
    function a<T extends IA>(person: T): T {
        return person
    }
    
    a<{
        sex: string,
        age: number
    }>({
        sex: "man",
        age: 18
    })
    /* {
        sex: "man",
        age: 18
    } */

泛型定义类

class a<T>{
    age: T;
    getAge: (person: string):T
}

const newA = new a<number>()
a.age = 18;

泛型定义接口

interface IA<T, U> {
    age: T,
    tall: T,
    name: U
}

const a:IA<number, string> = {
    age: 18,
    tall: 180,
    name: "HHG"
}

泛型定义type

type TA<T, U> = {
    name: T,
    age: U
} 
const a: TA<string, number> = {
    name: "HHG",
    age: 18
}

访问interface中的属性

interface a {
    name: string,
    age: number
}

type b = a["name"] // type b = string

extends和implements

extends 继承

继承就是子继承父的属性。

  • 类可以继承类
class a {
    name: string
}

class b extends a {
    age: number
}

const newB = new b({
    name: "HHG",
    age: 18
})
  • 接口可以继承类
class a {
    name: string
}
interface b extends a {
    age: number
}

const newB = new b({
    name: "HHG",
    age: 18
})

  • 接口可以继承接口
interface a {
    name: string
}
interface b extends a {
    age: number
}

const user:b = {
    name: "HHG",
    age: 18
}

implements 实现

implements只能用于class,也就是说只存在类实现接口或者类实现类。

  • 类实现接口
interface a {
    say: <T>(name: T):T
}

class b implements a {
    name: string,
    say: <T>(name: T): T => {
        console.log(name)
    }
}
  • 类实现类
class a {
    say: <T>(name: T):T => {
        console.log(name)
        return name
    }
}

class b implements a {
    say: <T, U>(name: T, age: U): void => {
        console.log(name, age)
    }
}

tsconfig.json和.d.ts

这是两个ts配置文件。

.d.ts

.d.ts的作用主要是在项目祖宗文件夹下定义全局的类型变量,并且在项目中不需要import就可以访问到。 实现上述功能还需要在tsconfig.json中的include中添加该文件,通常叫typings.d.ts

{
    "include": [
        "typings.d.ts"
    ]
}

declare

declare声明,在.d.ts文件中最外层的声明都需要加上declare或export,保证在项目内可以访问到。

在.d.ts中通常需要声明一些模块来避免因为不是js/ts语言类型的文件而导致的报错

declare module '*.css';
declare module '*.scss';
declare module '*.sass';
declare module '*.svg';
declare module '*.png';
declare module '*.jpg';
declare module '*.jpeg';
declare module '*.gif';
declare module '*.bmp';
declare module '*.tiff';
declare module 'omit.js';

declare还可以在.d.ts中声明一些没有ts的库避免报错。

declare module 'jquery';

declare还可以在.d.ts中声明全局变量,如react的node环境变量

declare const REACT_APP_ENV: string;

declare也可以声明全局都要频繁使用的类型,在项目中可以不需要引入就能使用

declare type = {
    name: string,
    age: number
}

还可以在模块内部再声明变量:

declare module '*.less' {
  const Style: Record<string, string>;
  export default Style;
}

import Style from "xxx.less"

<div className={Style.aaa}>
    // todo something
</div>

// types/foo-bar.d.ts

declare module 'foo' {
    export interface Foo {
        foo: string;
    }
}

declare module 'bar' {
    export function bar(): string;
}

// src/index.ts

import { Foo } from 'foo';
import * as bar from 'bar';

let f: Foo;
bar.bar();
declare就是告诉TS编译器你担保这些变量和模块存在,并声明了相应类型,编译的时候不需要提示错误!

tsconfig.json

tsconfig.json官方文档

编译配置 compilerOptions

compilerOptions

{

  "compilerOptions": {
    "target": "es5", // 输出代码类型,默认es3, 可选:`"ES5"`, `"ES6"``"ES2015"`, `"ES2016"`, `"ES2017"`或 `"ESNext"`"module": "esnext", // 指定生成哪个模块系统代码: `"None"`, `"CommonJS"`, `"AMD"`, `"System"`, `"UMD"`, `"ES6"`或 `"ES2015"`。 只有 `"AMD"`和 `"System"`能和 `--outFile`一起使用。`"ES6"`和 `"ES2015"`可使用在目标输出为 `"ES5"`或更低的情况下。
    "moduleResolution": "node", // 决定如何处理模块。或者是`"Node"`对于Node.js/io.js,或者是`"Classic"`(默认)。查看[模块解析](https://www.tslang.cn/docs/handbook/module-resolution.html)了解详情。
    "removeComments": false, // 删除所有注释,除了以 `/!*`开头的版权信息。
    "importHelpers": true, // 从 tslib导入辅助工具函数(比如 `__extends`, `__rest`等)
    "jsx": "react", // 在 `.tsx`文件里支持JSX: `"React"`或 `"Preserve"`。查看 [JSX](https://www.tslang.cn/docs/handbook/jsx.html)。
    "esModuleInterop": true, // 决定import的方式,如该项为false 就需要这么写:import * as React from 'react'
    "sourceMap": true, // 编译前后文件的映射
    "baseUrl": "./", // import时相对路径的起点(前缀),可以配合paths使用
    "strict": false, // 是否开启严格模式
    "paths": { // 路径别名映射
      "@/*": [
        "client/*"
      ]
    },
    "allowSyntheticDefaultImports": true // 与esModuleInterop配合使用,详情见:https://jishuin.proginn.com/p/763bfbd7a69f 
  }
}

文件配置

files

files表示需要对项目中哪些文件进行编译(根据编译规则compilerOptions进行编译),files中声明的文件只是入口文件。

但是当我们完成大型项目时,往往是有很多文件需要编译的,这时候可以用到include和exclud

include和exclud

include:表示包含这些文件的进行编译 exclud:表示编译时排除这些文件

  • exclude只对include有效,对files无效 声明时可以使用通配符:
  • * 匹配0或多个字符(不包括目录分隔符)
  • ? 匹配一个任意字符(不包括目录分隔符)
  • **/ 递归匹配任意子目录

比如想要让.d.ts中的声明全局访问, 就需要对其进行编译

  "includes": [
    "*.d.ts"
  ],

比如想要编译src目录下的所有文件

  "includes": [
    "src/*"
  ],
  • 如果 files 和 include 都未设置,那么除了 exclude 排除的文件,编译器会默认包含路径下的所有 TS 文件