TS特有de概念

元组

指定数组每个元素的类型

特点

  • 在初始化时,必须按限定的类型和个数赋值;
  • 能够通过数组的办法冲破限度;

示例

let arr: [string, number, any]arr = ['1', 1, 1] // OKarr = [1, 1, 1] // Errorarr = ['1', 1, 1, true] // Errorarr.push(true) // OK

接口

只是在ts编译阶段做类型查看,并不会转译成JS代码

用接口申明可调用的类型

示例

// 定义函数interface ISum {    (x: number, y: number, z?: number): number;}let sum: ISumsum = (a) => {//     ^ = OK  return 1}sum('1', 2)//   ^ Argument of type '"1"' is not assignable to parameter of type 'number'.// 定义类的new调用interface IConstructor {  new(arg: string): IConstructor;}function Constr (this: any, name: string) {  this.name = name}const instance = new (Constr as any as IConstructor)('111')

以上示例留神:

  • interface定义函数输出、输入的类型作为预置值内置在函数申明中

    • 函数申明时无需二次定义参数类型,函数输入值的类型推断要与interface定义的输入类型统一,否则会报错。
    • interface定义函数没有限度函数申明时传入的参数个数,只有在调用时才会报参数个数谬误;
  • 函数申明无奈间接其它类型,须要应用双重断言Constr as any as IConstructor

    • ==尽可能不要应用双重断言==,它会影响ts的判断
    // 示例:let num: number = 0let str: string = 's'num = str // Errornum = str as any as number // OK//^ = num === 's' //这里str认为是number类型,赋值胜利

联结类型

一个数据申明为联结类型,应用时,若不确定是联结类型中具体的类型时(通过if条件、as断言、in操作符、typeof放大未知范畴),只能拜访联结类型共有的办法。

断言

断言是联结类型放大未知范畴时应用,但,断言强制浏览器置信数据是咱们指定的类型,实际上是不平安的。【举荐】应用类型膨胀typeofinstanceof...来放大未知范畴。

当两个类型申明有交加时,才能够应用断言,否则会报错(because neither type sufficiently overlaps with the other.)

如果两个类型申明没有交加,能够应用双重断言强制断言成另一种类型,示例如上:Constr as any as IConstructor

readonly & Readonly泛型

readonly标识符,用于对象属性

Readonly映射类型,接管一个泛型T,用来把泛型T的所有属性标记为只读类型;

示例

type Foo = {  bar: number;  bas: number;}type ReadFoo = Readonly<Foo>/**     ^ = type ReadFoo = {*               readonly bar: number;*               readonly bas: number;*           }*/

示例

function fn(x: number | string): number {  return x.length;}// Property 'length' does not exist on type 'string | number'.// Property 'length' does not exist on type 'number'.

对象构造

示例

// type定义对象构造,不可重载type TObjectProps = {  x: number;  y: number;}const obj: TObjectProps = {  x: 1,  y: 1}// interface定义对象构造interface IObjectProps {  x: number;  y: number;}const obj: IObjectProps = {  x: 1,  y: 1,  add() {}  //^ = 'add' does not exist in type 'IObjectProps'}// interface定义重载interface IObjectProps {  x: number;  y: number;}const obj: IObjectProps = {  x: 1,  y: 1,  add() {} // OK}interface IObjectProps {  add: () => void;}// let & typeof定义对象构造,不可重载let objectProps: {  x: number;  y: number;}const obj: typeof objectProps = {  x: 1,  y: 1}

Function

函数类型申明形式有多种,利用场景两种: 固定参数,不固定参数;

示例

固定参数:
// type定义type Tfn = (a: number, b: number) => number;let fn1: Tfnfn1 = function(a, b) {  return  a + b}fn1(1, 2)// type定义重载type Tfn = {  (a: string): string;  (a: number, b: number): number;}let fn1: Tfnfn1 = function(a: any, b?: any): any {  if (b) {    return a + b  } else {    return a  }}fn1('1')//  ^ = let fn1: (a: string) => string (+1 overload)fn1(1, 2)//  ^ = let fn1: (a: number, b: number) => number (+1 overload)fn1(1)  //Error// interface定义interface Ifn {  (x: string): string;}let fn1: Ifnfn1 = function(a) {  return a}fn1('1')// interface定义重载interface Ifn {  (x: string): string;  (x: number, y: number): number;}let fn1: Ifnfn1 = function(a: any, b?: any): any {  if (b) {    return a + b  } else {    return a  }}fn1('1')//  ^ = let fn1: (a: string) => string (+1 overload)fn1(1, 2)//  ^ = let fn1: (a: number, b: number) => number (+1 overload)fn1(1)  //Error// let & typeof 申明let fn: {  (x: string): string;}let fn1: typeof fnfn1 = function(a) {  return a}fn1('1')// let & typeof申明重载let fn: {  (x: string): string;  (x: number, y: number): number;}let fn1: typeof fnfn1 = function(a: any, b?: any) {  if (b) {    return a + b  } else {    return a  }}fn1('1')//  ^ = let fn1: (a: string) => string (+1 overload)fn1(1, 2)//  ^ = let fn1: (a: number, b: number) => number (+1 overload)fn1(1)  //Error// function申明function fn(x: string): string {  return x}fn('1')// function申明重载:fn实现必须紧跟着fn头申明function fn(x: string): string;function fn(x: number, y: number): number;function fn(x: any, y?: any) {  if (y) {    return x + y  } else {    return x  }}fn('1')//  ^ = let fn: (a: string) => string (+1 overload)fn(1, 2)//  ^ = let fn: (a: number, b: number) => number (+1 overload)fn(1)  //Error

通过反复定义函数头实现重载,而函数申明输出、输入最好应用any类型(不应用any的话,函数体的操作逻辑应用的必须是联合声明类型共有的成员),调用时会依据参数个数匹配预约义的函数头进行校验。

不固定参数:
// 利用场景:作为回调函数,通过`apply`调用;interface IFn {  (...args: any[]): any;}function invoke(fn: IFn) {  fn.apply(null, [...arguments])}

枚举Enum

定义索引和值的双向映射;
enum Direction {  UP,  DOWN,  LEFT,  RIGHT}console.log(Direction[0]) // 'UP'console.log(typeof Direction[0]) // 'string'console.log(Direction['UP']) // 0console.log(typeof Direction['UP']) // 'number'console.log(Direction[0] === 'UP') // true

分类

数字枚举
数字枚举默认从0开始;

若有指定的索引,则后续数据索引++

// 场景:Code编码语义化enum Direction {  Up,  Down = 10,  Left,  Right}console.log(Direction[0]) // 'UP'console.log(Direction['Up']) // 0console.log(Direction['Left']) // 11console.log(Direction[10]) // 'Down'
字符串枚举
// 场景:游戏按键?enum Direction {  Up = 'u',  Down = 'd',  Left = 'l',  Right = 'r'}console.log(Direction['Up']) // '上'console.log(Direction['Down']) // '下'
常量枚举
和上述枚举类型编译后果不同;
enum Dir {  Up,  Down = 10,  Left,  Right}const enum Direction {  Up,  Down = 10,  Left,  Right}let directions = [  Direction.Up,  Direction.Down,  Direction.Left,  Direction.Right,];/////编译输入如下:"use strict";var Dir;(function (Dir) {    Dir[Dir["Up"] = 0] = "Up";    Dir[Dir["Down"] = 10] = "Down";    Dir[Dir["Left"] = 11] = "Left";    Dir[Dir["Right"] = 12] = "Right";})(Dir || (Dir = {}));let directions = [    0 /* Up */,    10 /* Down */,    11 /* Left */,    12 /* Right */,];

字面量类型

const str: 'name' = 'name' // str只能是'name'字符串,赋值其余字符串或其余类型会报错;const number: 1 = 1 // number只能是1,赋值其余数值或其余类型会报错;type Directions = 'UP' | 'DOWN' | 'LEFT' | 'RIGHT'let toWhere: Directions = 'LEFT' 

对应场景:

interface IO {  'y+': number;  'M+': number;  'd+': number;  'h+': number;  'm+': number;  's+': number;  'q+': number;  'S+': number;}type TKeyProps = keyof IO//  ^ = type TKeyProps = "y+" | "M+" | "d+" | "h+" | "m+" | "s+" | "q+" | "S+"var o: IO = {  'y+': this.getFullYear(),  'M+': this.getMonth() + 1,   'd+': this.getDate(),  'h+': this.getHours(),  'm+': this.getMinutes(),  's+': this.getSeconds(),  'q+': Math.floor((this.getMonth() + 3) / 3),  'S+': this.getMilliseconds()}o['y++'] // OKlet kkk = 's+'o[kkk] // Erroro[kkk as TKeyProps] // OK

泛型

泛型de目标是在成员之间(至多有两个中央用到了泛型占位)提供有意义的束缚

成员:

  • 类的属性
  • 类的办法
  • 函数参数
  • 函数返回值
泛型在定义时,不能明确数据类型,申明一变量占位,调用时通过传入类型或ts类型推断来确定具体的数据类型。
  • 绝对于any:泛型未失落数据结构信息;
  • 绝对于联合声明:泛型明确具体的类型构造,联合声明并未明确具体类型;
逻辑中只能调用泛型数据的通用成员属性/办法,否则会报错;

示例

function identity<T>(arg: T): T {    return arg;}// 明确指定T是string类型let output = identity<string>("myString");  // type of output will be 'string'// 利用类型推论 -- 即编译器会依据传入的参数主动地帮忙确定T的类型let output = identity("myString");  // type of output will be 'string'

any & never & unknown

  • any

    • 称为top type,任何类型的值都能赋给any类型的变量
    • 又称为bottom type,任何类型(除never外)的子类型
    • 能够了解为没有类型
  • never

    • 称为bottom type,任何类型的子类型,也能够赋值给任何类型
    • 推断场景1:

      无奈执行到函数终止点的函数表达式
      • 场景:总抛出异样的函数表达式的返回值类型;
        // 须要是函数表达式,若是函数申明为assertNever: () => void  const assertNever = function (x: any) {  //      ^ = type assertNever = never    throw new Error("Unexpected object: " + x);  }
      • 场景:永不完结函数表达式的返回值类型;
        let loop = function () {  //    ^ = type loop = never    while (true) {}  }
    • 推断场景2:被永不为真的类型爱护束缚的变量类型;
      //  示例:    type result = 1 & 2 // 后果为never    //      ^ = type result = never  // 尤大示例:    interface Foo {      type: 'foo'    }      interface Bar {      type: 'bar'    }      type All = Foo | Bar    function handleValue(val: All) {      switch (val.type) {        case 'foo':          // 这里 val 被收窄为 Foo          break        case 'bar':          // val 在这里是 Bar          break        default:          const exhaustiveCheck = val          //      ^ = type exhaustiveCheck = never          break      }    }
  • unknown

    • top type:任何类型都是它的subtype
    • 不能将unknown赋值其它类型,unknown类型的数据只能赋值给unknownany类型;
    • 绝对于anyts会为unknown提供无效的类型检测;
    • unknown类型数据的属性或办法,须要通过类型断言/类型膨胀来放大未知范畴;

any的危害

应用any做类型申明或者做断言,会丢失原始数据的构造类型信息,即:再也无奈晓得原始数据是什么构造了,更不会有报错信息,举荐应用unknown & 类型膨胀

索引签名

索引签名用于定义对象/数组de通配构造

索引签名的名称(如:{ [key: string]: number }key )除了可读性外,没有任何意义,能够任意定义。

其它成员都必须合乎索引签名值de构造,所以,索引签名值de构造必须是其它成员属性类型的top type

尽量不要应用字符串索引签名与无效变量混合应用——如果属性名称中有拼写错误,这个谬误不会被捕捉。【同级的其它属性应该是对索引签名的限度加强

示例

// 1. 对象示例interface Foo {  [key: string]: number; // 该通配构造[key: string]即是签名索引。}// 1. 数组示例interface Foo {  [idx: number]: string;  length: number;}const arr: Foo = ['1', '2', '3', '4']// 2. 所有明确的成员都必须合乎索引签名  interface Bar {    [key: string]: number;    x: number; // OK    y: string;   //^ = Property 'y' of type 'string' is not assignable to string index type 'number'.  }// 2. 能够应用穿插类型冲破限度  interface Bar {    [key: string]: number;    x: number;   }  type Composition = Bar && {    y: string;    }// 3. 无效变量和索引签名不要同级定义interface CSS {  [selector: string]: string;  color?: string; }const failsSilently: CSS = {  colour: 'red' // 'colour' 不会被捕捉到谬误};

TS类型申明

ts报错对应查找:做词典用;

declare申明通过各种路径(<script>导入、new webpack.ProvidePlugin({//...}))引入全局命名空间的变量/模块

函数申明中显式应用this

// 场景示例:function Person() {  var vm = this;  //         ^ = this具备隐式类型any;}// 解决方案:function Person(this: void) {  var vm = this;}

this作为函数的第一参数,因为ts只是类型查看,编译成JS时不会将this参数输入。

全局库申明

// 场景示例:$('div')[]//^ = not find name '$'// 解决方案:追加zepto.d.ts申明interface ZeptoStatic {  //...}interface ZeptoCollection {  // ...}declare var Zepto: (fn: ($: ZeptoStatic) => void) => void;declare var $: ZeptoStatic;declare function $(selector?: string, context?: any): ZeptoCollection;

JS内置对象追加属性申明

// 场景示例:Date.prototype.format = function() {  //...}const arg = 2423423413;new Date().format(arg)//          ^ = format 不在Date上 // 解决方案:追加申明*.d.ts申明declare global {  interface Date {    Format(arg: string): string;  }}

图片...动态资源申明

// 场景示例:import Logo from './assets/logo.png'//      ^ = not find Module// 解决方案:追加申明*.d.ts申明declare module '*.svg'declare module '*.png'declare module '*.jpg'declare module '*.jpeg'declare module '*.gif'declare module '*.bmp'declare module '*.tiff'

vue3配置全局成员

// 场景示例:// main.tsimport { createApp } from 'vue'import axios from 'axios'import qs from 'qs'import App from './App.vue'const vueInstance = createApp(App)vueInstance.config.globalProperties.$http = axiosvueInstance.config.globalProperties.$qs = qs// *.vue...this.$http.post(...).then(() => {})//    ^ = Property '$http' does not exist on type 'ComponentPublicInstance<>'......// 解决方案:追加申明*.d.tsimport Axios from "axios";import qs from 'qs'import Store from "../store";declare module '@vue/runtime-core' {  interface ComponentCustomProperties {    $http: Axios;    $store: Store;    $qs: qs;  }}

第三方ES Module:export default申明

// 场景示例:// utils.tsexport default (function() {  const utils = {        }    return utils})() // *.tsutils.default.isString(123)//    ^ = Property 'default' does not exist on type 'typeof utils'// 解决方案:追加申明*.d.tsdeclare namespace utils {  const UtilsProps: {    isString: (arg: unknown) => boolean;    isArray: (arg: unknown)=> boolean;    isObject: (arg: unknown)=> boolean;    isDate: (arg: unknown)=> boolean;    app: any;  }  export default UtilsProps;}

第三方ES Module:export申明

// *.d.tsdeclare namespace utils {  export function isString (arg: unknown): boolean;  export var app: any;}

第三方CommonJS模块申明

// 模块类库  module-lib.tsfunction moduleLib(options) {   console.log(options);}const version = "1.0.0";function doSomething() {   console.log('moduleLib do something');}moduleLib.version = version;moduleLib.doSomething = doSomething;module.exports = moduleLib;// *.d.tsdeclare function moduleLib(options: Options): void;interface Options {   [key: string]: any,}declare namespace moduleLib{   const version: string;   function doSomething(): void;}export = moduleLib;

第三方'UMD'申明

// UMD库  umd-lib.js(function (root, factory) {   if (typeof define === "function" && define.amd) {      define(factory);   } else if(typeof module === "object" && module.exports) {      module.exports = factory();   } else {      root.umdLib = factory();   }})(this, function () {   return {      version: "1.0.2",      doSomething() {         console.log('umdLib do something');      }   }});// *.d.ts文件declare namespace umdLib {   const version: string;   function doSomething(): void;}export as namespace umdLib // 专门为umd库筹备的语句,不可短少export = umdLib // commonjs导出

参考书

  • 在线编译工具
  • 深刻了解TypeScript
  • ts官网
  • w3c/ts
  • ...