ts中内置类型

154 阅读5分钟

ts是超集,总所周知。所以也就出现了几个没见过的类型和使用。

declare

先把变量(基本变量,type,interface,calss ENum,函数,模块,命名空间)描述了;告诉编译器某个类型是存在的,不用给出具体实现。只能用来描述已存在的变量和数据结构,不能用来声明新的。 declary不能赋值,只能定义类型。

declare let k:number = 3; // 报错
declare let k = 5;
declare var a:number
a = 3
declare function sayL(name:string):void;
sayH('x')
// 这样做的好处是定义了这个汗出,并且后续可以更改sayL这个函数

declare变量的主要作用还是承接全局变量,比如window下的变量,你要在t.js中使用window.x,但是x没被定义过,也不好定义window,所以就直接declare let x:number,这样还能修改。 declare最主要的作用还是在module中的使用、。

declare namespace AnimalLib {
  class Animal {
    constructor(name:string);
    eat():void;
    sleep():void;
  }

  type Animals = 'Fish' | 'Dog';
}

// 或者
declare module AnimalLib {
  class Animal {
    constructor(name:string);
    eat(): void;
    sleep(): void;
  }

  type Animals = 'Fish' | 'Dog';
}
// 使用了declare后。这两个里面的变量不需要使用export来暴露了

这里脚本a.ts定义了一个接口A,脚本b.ts为这个接口添加了属性y。declare module './a' {}表示对a.ts里面的模块,进行类型声明,而同名 interface 会自动合并,所以等同于扩展类型。实质是新增了一个同名的接口,因为是同名会自动合并。

// a.ts
export interface A {
  x: number;
}
export default interface D{
  
}
// b.ts
import { A } from './a';

declare module './a' {
  interface A {
    y: number;
  }
  // interface D {},无法拓展,会报错,因为是default的
  // interface B {},无法定义,会报错,只能改A,不能新建顶层
}

const a:A = { x: 0, y: 0 };
// declare module NAME语法里面的模块名NAME,跟 import 和 export 的模块名规则是一样的,且必须跟当前文件加载该模块的语句写法(上例import { A } from './a')保持一致

上述使用module操作需要注意:

  1. declare module 描述的模块名可以使用通配符。模块名my-plugin-*表示适配所有以my-plugin-开头的模块名(比如my-plugin-logger)。
  2. 不能创建新的顶层类型。也就是说,只能对a.ts模块中已经存在的类型进行扩展,不允许增加新的顶层类型,比如新定义一个接口B
  3. 不能对default暴露的接口拓展,只能对 export 命令输出的命名接口进行扩充。这是因为在进行类型扩展时,需要依赖输出的接口名。declaremodule"模块名";declaremodule"hot-new-module";
declare module 'my-plugin-*' {
  interface PluginOptions {
    enabled: boolean;
    priority: number;
  }

  function initialize(options: PluginOptions): void;
  export = initialize;
}

// 一些在原形上加方法的情况,则需要declare重写了,如下
declare global {
  interface String {
    toSmallString(): string;
  }
}
String.prototype.toSmallString = ():string => {
  // 具体实现
  return '';
};

类型声明文件。.d.ts 使用时,都需要使用三斜杠命令, 加一个类型声明文件 /// 类似于

// node.d.ts, url和path都是单独的模块脚本,就定义在这里。
declare module "url" {
  export interface Url {
    protocol?: string;
    hostname?: string;
    pathname?: string;
  }
  export function parse(
    urlStr: string,
    parseQueryString?,
    slashesDenoteHost?
  ): Url;
}
declare module "path" {
  export function normalize(p: string): string;
  export function join(...paths: any[]): string;
  export var sep: string;
}

如果没有上面这一行命令,自己的脚本使用外部模块时,就需要在脚本里面使用 declare 命令单独给出外部模块的类型。

enum

就是定义枚举,例如http状态码,需要注意的是,前面加个const或者let那就不一样了

enum StatusCode {
  error = 400,
  success = 200,
}
const enum requestWrongCodes {
  missingParameter = "A",
  wrongParameterType = "B",
  invalidToken = "C",
}
const k: StatusCode = 400;
const w: StatusCode = StatusCode.error;

const j: requestWrongCodes = requestWrongCodes.invalidToken;
// const q: requestWrongCodes = "C"; // 这个就会报错,不能将类型“"C"”分配给类型“requestWrongCodes”。

console.log(k, w, j);
// console.log(q)

enum Language {
  English,
  Chinese,
  Spanish,
}

console.log(Language, Language.Chinese, Language[0]);
const enum requestWrongCodes1 {
  missingParameter,
  wrongParameterType,
  invalidToken,
}
// console.log(requestWrongCodes1);// 报错,因为"const" 枚举仅可在属性、索引访问表达式、导入声明的右侧、导出分配或类型查询中使用
// console.log(requestWrongCodes1[0]); // 报错,只有使用字符串文本才能访问常数枚举成员

enum Language {
  English,
  Chinese,
  Russian,
}
console.log(Language, Language.English, Language[0])
//这里推导出来的结果是
{
  '0': 'English',
  '1': 'Chinese',
  '2': 'Russian',
  English: 0,    
  Chinese: 1,    
  Russian: 2     
}
record

接收两个泛型参数;record后面的泛型就是对象键值对类型; 作用:定义一个对象的key和value类型

Record<key type, value type>;
type Record<K extends string|number|symbol, T> = {
  [P in K]: T;
}

大部分情况,我们都配合enum, type使用,不然意义不大了。

enum Language {
  English,
  Chinese,
  Russian,
}
const obj: Record<Language, number[]> = {
  [Language.English]: [1],
  [Language.Spanish]: [1],
  [Language.Chinese]: [1],
}; // 配合enum的话,必须把每一个枚举都列出来,不然就报错
type K = Language.English | Language.Spanish | Language.Chinese;
// const o1: Record<K, number> = {
//   // 报错,因为record会限制你把所有K的可能输入,所以要像上面一样
//   [Language.English]: 1,
// };
// console.log(o1)

type Q = number | string;
const o2: Record<Q, number> = {
  // 报错,因为record会限制你把所有K的可能输入,所以要像上面一样
  [Language.English]: 1,
};
console.log(obj, o2);
Partial
type Partial<T> = {
    [P in keyof T]?: Partial<T[P]>
}

生成一个新类型,该类型与 T 拥有相同的属性,但是所有属性皆为可选项,T可以是type或者interface

Required

用法和上面一样,生成一个新类型,该类型与 T 拥有相同的属性,但是所有属性皆为可选项

Readonly

用法和上面一样,生成一个新类型,该类型与 T 拥有相同的属性,但是不可更改了

interface Foo {
    name: string
    age: number
}
type Bar = Readonly<Foo>
// 相当于
type Bar = {
    readonly name: string
    readonly age: number
}
Pick,Omit

和lodash的pick,omit一样

type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
};
interface Foo {
    name: string;
    age?: number;
    gender: string;
}
type Bar = Pick<Foo, 'age' | 'gender'>


type Exclude<T, U> = T extends U ? never : T
Exclude

取反集

type Exclude<T, U> = T extends U ? never : T
type A = number | string | boolean
type B = number | boolean
type Foo = Exclude<A, B>
// 相当于
type Foo = string
Extract

和exclude相反,取交集

NonNullable

从泛型 T 中排除掉 null 和 undefined

Parameters

以元组的方式获得函数的入参类型,注意,不是生成,是用来获取的

type t = Parameters<(name: string) => any>; // type t = [string]
type t2 = Parameters<((name: string) => any)  | ((age: number) => any)>; // type t2 = [string] | [number]
ConstructorParameters

同上,但是是获取构造函数的入参类型,注意,不是生成,是用来获取的

type t = ConstructorParameters<(new (name: string) => any)  | (new (age: number) => any)>;
// type t = [string] | [number]
returnType

获得函数返回值类型,注意,不是生成,是用来获取的,我不知道一个函数返回,我用return去得到,再给自己用

type t = ReturnType<(name: string) => string | number>
// type t = string | number
instanceType

获取构造函数返回值的类型

type t = InstanceType<new (name: string) => {name: string, age: number}>
/* 
type t = {
    name: string;
    age: number;
}
*/