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放大未知范畴),只能拜访联结类型共有的办法。
断言
断言是联结类型放大未知范畴时应用,但,断言强制浏览器置信数据是咱们指定的类型,实际上是不平安的。【举荐】应用类型膨胀typeof
、instanceof
...来放大未知范畴。当两个类型申明有交加时,才能够应用断言,否则会报错(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
类型的数据只能赋值给unknown
、any
类型; - 绝对于
any
,ts
会为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
- ...