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操作需要注意:
- declare module 描述的模块名可以使用通配符。模块名my-plugin-*表示适配所有以my-plugin-开头的模块名(比如my-plugin-logger)。
- 不能创建新的顶层类型。也就是说,只能对a.ts模块中已经存在的类型进行扩展,不允许增加新的顶层类型,比如新定义一个接口B
- 不能对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;
}
*/