共计 10131 个字符,预计需要花费 26 分钟才能阅读完成。
TS 特有 de 概念
元组
指定数组每个元素的类型
特点
- 在初始化时,必须按限定的类型和个数赋值;
- 能够通过数组的办法冲破限度;
示例
let arr: [string, number, any] | |
arr = ['1', 1, 1] // OK | |
arr = [1, 1, 1] // Error | |
arr = ['1', 1, 1, true] // Error | |
arr.push(true) // OK |
接口
只是在
ts
编译阶段做类型查看,并不会转译成JS
代码用接口申明可调用的类型
示例
// 定义函数 | |
interface ISum {(x: number, y: number, z?: number): number; | |
} | |
let sum: ISum | |
sum = (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 = 0 let str: string = 's' num = str // Error num = 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: Tfn | |
fn1 = function(a, b) {return a + b} | |
fn1(1, 2) | |
// type 定义重载 | |
type Tfn = {(a: string): string; | |
(a: number, b: number): number; | |
} | |
let fn1: Tfn | |
fn1 = 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: Ifn | |
fn1 = function(a) {return a} | |
fn1('1') | |
// interface 定义重载 | |
interface Ifn {(x: string): string; | |
(x: number, y: number): number; | |
} | |
let fn1: Ifn | |
fn1 = 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 fn | |
fn1 = function(a) {return a} | |
fn1('1') | |
// let & typeof 申明重载 | |
let fn: {(x: string): string; | |
(x: number, y: number): number; | |
} | |
let fn1: typeof fn | |
fn1 = 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']) // 0 | |
console.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']) // 0 | |
console.log(Direction['Left']) // 11 | |
console.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++'] // OK | |
let kkk = 's+' | |
o[kkk] // Error | |
o[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.ts | |
import {createApp} from 'vue' | |
import axios from 'axios' | |
import qs from 'qs' | |
import App from './App.vue' | |
const vueInstance = createApp(App) | |
vueInstance.config.globalProperties.$http = axios | |
vueInstance.config.globalProperties.$qs = qs | |
// *.vue | |
... | |
this.$http.post(...).then(() => {}) | |
// ^ = Property '$http' does not exist on type 'ComponentPublicInstance<>'... | |
... | |
// 解决方案:追加申明 *.d.ts | |
import 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.ts | |
export default (function() {const utils = {} | |
return utils | |
})() | |
// *.ts | |
utils.default.isString(123) | |
// ^ = Property 'default' does not exist on type 'typeof utils' | |
// 解决方案:追加申明 *.d.ts | |
declare 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.ts | |
declare namespace utils {export function isString (arg: unknown): boolean; | |
export var app: any; | |
} |
第三方 CommonJS
模块申明
// 模块类库 module-lib.ts | |
function 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.ts | |
declare 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
- …