关于前端:TS使用归档

54次阅读

共计 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 放大未知范畴),只能拜访联结类型共有的办法。

断言

断言是联结类型放大未知范畴时应用,但,断言强制浏览器置信数据是咱们指定的类型,实际上是 不平安 的。【举荐】应用类型膨胀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: 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类型的数据只能赋值给 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.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

正文完
 0