如果我们期望根据属性类型动态选取函数调用,可以书写如下:
type DescribableFunction = {description: string;(someArg: number): boolean;};function doSomething(fn: DescribableFunction) {console.log(fn.description + " returned " + fn(6));}
在这里,fn(6)
中传入了数字 6,因而其命中了对象中 (someArg: number): boolean;
该条规则。
function firstElement1<Type>(arr: Type[]) {return arr[0];}function firstElement2<Type extends any[]>(arr: Type) {return arr[0];}// ✅ a: numberconst a = firstElement1([1, 2, 3]);// ❎ b: anyconst b = firstElement2([1, 2, 3]);
粗看 firstElement1 与 firstElement2 似乎达到一样的效果,但是结果上 a 的类型是 number,b 的类型是 any。这是因为 TypeScript 必须使用约束类型解析 arr[0] 表达式,而非在调用期间“等待”解析元素。
在以下代码中,在定义侧对回调函数中的参数作了可选表达式的声明,且在定义侧调用的 callback 函数未传入可选参数,因此在消费者调用可选参数值时报了错误。
// ❎ 定义侧错误示范function myForEach(arr: any[], callback: (arg: any, index?: number) => void) {for (let i = 0; i < arr.length; i++) {callback(arr[i])}}// 消费侧myForEach([1, 2, 3], (a, i) => {// Object is possibly 'undefined'.console.log(i.toFixed())})
与之对应的,若需要在回调函数中定义参数的类型,尽量不使用可选参数表达式,除非消费该函数时能保证处理好可选参数。
// ✅ 定义侧正确示范function myForEach(arr: any[], callback: (arg: any, index: number) => void) {for (let i = 0; i < arr.length; i++) {callback(arr[i], i)}}// 消费侧myForEach([1, 2, 3], (a, i) => {console.log(i.toFixed())})
函数实现侧必须与定义重载侧对应。
// 定义侧function fn(x: boolean): void;function fn(x: string): void;// 实现侧function fn(x: boolean) { } // ❎function fn(x: boolean | string) { } // ✅
// 定义侧function fn(x: string): string;function fn(x: number): boolean;// 实现侧// ❎function fn(x: string | number) {return "oops"}// ✅function fn(x: string | number): string | boolean {if (typeof x === 'string') {return 'hhh'} else if (typeof x === 'number') {return true}}
在 TypeScript 中,数组被认为是可变的,因而会导致一些奇怪的问题。
const args = [8, 5]// ❎ A spread argument must either have a tuple type or be passed to a rest parameter.const angle = Math.atan2(...args)
此时,通常最为直接的解决方式是使用 const
来约束目标为不可变对象。
Readonly
与Const
都有不可让值修改的作用。区别在于 Readonly 作用于属性, Const 作用于变量。
const args = [8, 5] as constconst angle = Math.atan2(...args)