使用TS已有半年之久,年底了,总结下项目中,个人使用TS的一些心得技巧,欢迎留言吐槽。
1. 参考lodash实现的 get 方法:
function get<T extends Object, Key extends keyof T>(obj: T, key: Key ) {
return obj[key];
}
2. set 方法实现:
function set<T extends Data, Key extends keyof T,>(obj: T, key: Key, value: T[Key]) {
obj[key] = value;
return obj;
}
可以基于set的实现,统一处理表单数据,简化设置state的代码,以下是在React中的运用:
3. async await 的TS 封装
从 await-to-js 中学到的异步优雅封装。 当我们发起异步请求,(例如接口请求),对于成功和失败的数据。 该函数统一返回一个数组,
- 第一个元素表示错误,成功返回null, 失败返回 继承自Error的 对象,
- 第二个参数表示Promise.resolve() 的成功的数据, 失败返回null.
export function awaitTo<T, U = Error> ( promise:
Promise<T>, errorExt?: object): Promise<[U, undefined] | [null, T]> {
return promise.then<[null, T]>((data: T) =>
[null, data]).catch<[U, undefined]>((err: U) => {
if (errorExt) {
Object.assign(err, errorExt);
}
return [err, undefined];
});
}
// 使用方法:
const [error, dataList] = await awaitTo<DataResponse>(promiseObj);
深度Partial
Partial作用是将传入的属性变为【可选项】,不过TS内置的只支持第一层,第二层以后就不会处理了, 如果要处理多层,就只能自定义DeepPartial. 依靠递归来解决。如下:
export type DeepPartial<T> = {
[key in keyof T]? : T[key] extends Object ? DeepPartial<T[key]> : T[key]
}
any 和 unknow
- any 类型本质上是类型系统的一个逃逸舱。完全忽略typescript检查,简单粗暴。
- unknow 是 ts3.0版本新增特性,和any相比,更加严格。 unknown 类型只能
被赋值,或给 any 类型和 unknown 类型本身. 有些同学会好奇,什么时候用any,什么时候用unkonw ? 以下是我个人的总结:
any使用场景:
- 历史JS 项目迁移
- 调用引用某第三方的,类和实例,未生命TS类型定义(慎之又慎)
unknow 使用场景,看示例:
let value: unknown;
value = true; // OK
value = 42; // OK
value = "Hello World"; // OK
value = []; // OK
value = {}; // OK
value = Math.random; // OK
value = null; // OK
value = undefined; // OK
value = new TypeError(); // OK
value = Symbol("type"); // OK
let value: unknown;
value.foo.bar; // Error
value.trim(); // Error
value(); // Error
new value(); // Error
value[0][1]; // Error
如果需要对unknow 类型,做如下操作:
let value: unknown;
value ='1&2'; // ok
value.split('&') // error
虽然 value 赋值成功,但value.splice 却未通过编译。如果明确类型,可以通过 as(类型断言) 来处理.
(value as string).split('&') // ok
断言!
! 的作用是断言某个变量不会是 null/undefined,告诉编辑器停止报错。
const obj = {
name: 'loy'
}
const a = obj!.name; // 假设 obj是你从后端获取的获取
确定 obj.name 一定是存在的且不是null/undefined,使用! 只是消除编辑器报错,不会对运行有任何影响。
属性或者参数中使用 !,表示强制解析(告诉 typescript 编译器,这里一定有值); 变量后使用 !: 表示类型推荐排除 null/undefined。
再看以下例子;
function myFunc(maybeString: string | undefined | null) {
// Type 'string | null | undefined' is not assignable to type 'string'.
// Type 'undefined' is not assignable to type 'string'.
const onlyString: string = maybeString; // Error
const ignoreUndefinedAndNull: string = maybeString!; // Ok
}
?? 空值合并
在 TypeScript 3.7 版本中除了引入了前面介绍的可选链 ?. 之外,也引入了一个新的逻辑运算符 —— 空值合并运算符 ??。当左侧操作数为 null 或 undefined 时,其返回右侧的操作数,否则返回左侧的操作数。
与逻辑或 || 运算符不同,逻辑或会在左操作数为 falsy 值时返回右侧操作数。也就是说,如果你使用 || 来为某些变量设置默认的值时,你可能会遇到意料之外的行为。比如为 falsy 值(''、NaN 或 0)时。
这里来看一个具体的例子:
// 以前的处理方式, 给默认值
const params = params || 'defalut value';
console.log('params: ', params);
// 我们希望如果参数是 null 或者 undefined 才赋值 默认参数。, 用空值合并运算符 来解决
const foo = null ?? 'default string';
console.log(foo); // 输出:"default string"
const baz = 0 ?? 42;
console.log(baz); // 输出:0
以上 TS 代码经过编译后,会生成以下 ES5 代码:
"use strict";
var _a, _b;
var foo = (_a = null) !== null && _a !== void 0 ? _a : 'default string';
console.log(foo); // 输出:"default string"
var baz = (_b = 0) !== null && _b !== void 0 ? _b : 42;
console.log(baz); // 输出:0
通过观察以上代码,我们更加直观的了解到,空值合并运算符是如何解决前面 || 运算符存在的潜在问题。下面我们来介绍空值合并运算符的特性和使用时的一些注意事项。
当空值合并运算符的左表达式不为 null 或 undefined 时,不会对右表达式进行求值。
function A() { console.log('A was called'); return undefined;}
function B() { console.log('B was called'); return false;}
function C() { console.log('C was called'); return "foo";}
console.log(A() ?? C());
console.log(B() ?? C());
// 上述代码运行后,控制台会输出以下结果:
/*
A was called
C was called
foo
B was called
false
*/
如何将外部类型导入全局 .d.ts 文件
使用以下任一方法:
interface Test {
date: import('moment').Moment;
}
或者
type Moment = import('moment').Moment;
interface Test {
date: Moment;
}
注意:
当文件具有顶级import或export语句时,它将被视为一个模块。不再作为全局模块。