前段时间项目忙,加之疫情原因,就没有更新。😊😊😊
这篇文章主要是类型声明过程中遇到的需求和问题做的记录。
持续更新中…
如何编写.d.ts声明文件?
.d.ts文件是在编写typescript项目中必不可少的,它是让我们能在ts中调用的js的声明文件。比如有很多主流的npm库都是基于js编写的,那么在你的ts项目中引用时,会提示你找不到对应包或函数的声明,此时并不需要我们用ts对组件重写,只需在你的项目中编写包含该库类型声明的.d.ts文件即可。
通常在定制化编写组件时,.d.ts会统一放到@types或者typings文件夹下,当然你也可以不写.d.ts,而通过tsc对ts文件做编译生成对应文件的.d.ts。
declare var声明全局变量declare function声明全局方法declare class声明全局类declare enum声明全局枚举类型declare namespace声明(含有子属性的)全局对象interface和type声明全局类型export导出变量export namespace导出(含有子属性的)对象export defaultES6 默认导出export =commonjs 导出模块export as namespaceUMD 库声明全局变量declare global扩展全局变量declare module扩展模块///三斜线指令
eg. 扩展 axios 模块请求的 config 配置。 axios.interceptors.response. errors.config 亦可拿到
declare module 'axios' {
export interface AxiosRequestConfig {
metadata?: {
start: number;
};
/**
* 用于控制该请求出现错误时, 是否由默认message显示错误信息
*
* @type {boolean}
* @memberof AxiosRequestConfig
*/
suppressError?: boolean;
/**
* 用于请求在401状态码时, 是否自动重定向到登陆页。
*
* @type {boolean}
* @memberof AxiosRequestConfig
*/
suppressUnauthRedirect?: boolean;
}
}
声明语句中只能定义类型,切勿在声明语句中定义具体的实现
///三斜线指令
/// <reference path=”…” />
三斜线引用告诉编译器在编译过程中要引入的额外的文件。三斜线指令仅可放在包含它的文件的最顶端。 一个三斜线指令的前面只能出现单行或多行注释。
declare namespace详解
namespace中文称命名空间,现在已经不建议再使用ts中的namespace,而推荐使用ES6的模块化方案了,虽然目前仍能使用。
eg. jQuery提供了一个jQuery.ajax方法可以调用,那么我们就应该使用declare namespace jQuery来声明这个拥有多个子属性的全局变量。
declare namespace jQuery {
function ajax(url: string, settings?: any): void;
const version: number;
class Event {
blur(eventType: EventType): void;
}
enum EventType {
CustomClick,
}
}
注意,在declare namespace内部,我们直接使用function ajax来声明函数,而不是使用declare function ajax。类似的,也可以使用 const, class, enum等语句`
- 嵌套的命名空间
使用场景: 诸如 jQuery.fn.extend({})
declare namespace jQuery {
function ajax(url: string, settings?: any): void;
namespace fn {
function extend(object: any): void;
}
}
- 当然你也可以这样写:
// 这样写有很多局限性, 比如 jQuery不仅仅有 fn属性的时候
declare namespace jQuery.fn {
function extend(object: any): void;
}
type和interface如何抉择?
推荐任何时候都是用type,type使用起来更像一个变量,与interface相比,type的特点如下:
- 表达功能更强大,不局限于
object/class/function - 要扩展已有
type需要创建新type,不可以重名 - 支持更复杂的类型操作
基本上所有用interface表达的类型都有其等价的type表达。
给函数添加属性申明
interface FuncWithAttachment {
(param: string): boolean;
someProperty: number;
}
const testFunc: FuncWithAttachment = (param) => {};
const result = testFunc('mike'); // 有类型提醒
testFunc.someProperty = 3; // 有类型提醒
等价的声明合并写法:
// 组合多个声明语句,它们不会产生冲突
type FuncWithAttachment = (param: string) => boolean;
type FuncWithAttachment = {
someProperty: number;
};
枚举类型声明及类型使用
// 联合类型
type A = 'a' | 'b' | 'c';
// 枚举类型
enum A {
'a' = 'a',
'b' = 'b',
'c' = 'c',
}
注意 (type)A 等价 keyof typeof (enum 的 keys)A,如果想使用 (enum 的 values 的联合类型) 直接使用 A 即可
泛型的其他使用场景
列举除了泛型函数外的一些常见使用场景:
- 接口返回数据类型约束
// 有这么一个 interface ,显然, 很多接口返回数据都是这种结构,所以这里使用了 泛型约束
interface A<T> {
page: number;
size: number;
results: T[];
total: number;
}
interface B {
name: string;
age?: number;
}
const res = await request<A<B>>('/api/xx');
Promise类型
在做异步操作时我们经常使用 async 函数,函数调用时会 return 一个 Promise 对象,可以使用 then 方法添加回调函数。
Promise 是一个泛型类型,T 泛型变量用于确定使用 then 方法时接收的第一个回调函数(onfulfilled)的参数类型。
// 某 props 接收异步处理函数
onTaskRequest?: (args: API.X[]) => Promise<API.X[]>;
event事件类型
// click 使用 React.MouseEvent 加 dom 类型的泛型
// HTMLInputElement 代表 input标签 另外一个常用的是 HTMLDivElement
const onClick = (e: React.MouseEvent<HTMLInputElement>) => {};
- 作为组件
Props及hooks参数
interface FnComProps {}
const FnCom: React.FC<FnComProps> = (props) => {
return <>组件</>;
};
…
动态更新 Object 的 key
type Person = {
name: string;
age?: number;
[k: string]: any; // 指定 k 可以为任意的 string
};
反向使用 typeof
// NewsCard 组件接收的 props 类型
interface NewsCardProps {
// ficusService.getNews: (params: API.NewsQuery) => Promise<API.PaginationNewsResult<API.News>>
// 通过 typeof 反向获取类型
provider: typeof ficusService.getNews;
}
巧用Partial
使用 Partial 将所有的 props 属性都变为可选值。—— 这对我们在接口定义时很有用
type Partial<T> = { [P in keyof T]?: T[P] };
// 在 A 接口中, 我们使用 interface X 完成了对某数据的返回类型定义。
// 在 B 接口中, 我们可能会对 X 中定义的字段的部分字段进行更新, 但在 A 接口 为必选字段, B 接口为可选
export const conditionService = {
async query(query: Query) {
const res = await axios.get<PageResult<ConditionTemp>>(`/v1.0/condition`, {
params: query,
});
return res.data;
},
async update(data: Partial<ConditionTemp>) {
await axios.put(`/v1.0/condition`, data);
},
};
为 Window 添加参数
使用第三方库时(ga),ga 是全局方法,在使用时会提示” 类型“Window”上不存在属性“ga”
interface Window {
ga: (
command: 'send',
hitType: 'event' | 'pageview',
fieldsObject: GAFieldsObject | string,
) => void;
reloadAuthorized: () => void;
}
不想在 Window 中增加,但是想要全局使用,比如通过 define 注入的参数,我们通过 declare 关键字在 /src/typings.d.ts 注入。
declare const REACT_APP_ENV: 'test' | 'dev' | 'pre' | false;
装包没有相对应的 @types
安装包对应的
@types文件包,如@types/react、@types/react-dom自定义其为
any。
declare module 'xxx'; // d.ts
import xxx from 'xxx'; // tsx
@ts-ignore
// @ts-ignore
xxx;
遇到动态性比较强的代码,不妨使用 as unknown as XXX