乐趣区

关于javascript:TypeScript-的-Substitutability

Substitutability 中文含意是 可代替性,这个词我未在 TypeScript 的语言个性相干文档上看到,百度、谷歌搜寻也寥寥无几。仅在 TypeScript FAQ 找到相干形容。

无关类型零碎的许多答案都提到了可替代性。这是一个准则,即如果能够应用对象 X 代替某些对象 Y,则XY的子类型。咱们通常也说 X 能够调配给 Y(这些术语在TypeScript 中的含意略有不同,然而 区别在这里并不重要)。

这段形容很好了解,大体就是子类型能够用在父类型呈现的中央。但理论波及的 TypeScript 应用场景,和这个词不是很符合,兴许是语言的差别,中文含意不便于了解。

理论 Substitutability 解决的场景是:TypeScript 容许 function 作为回调函数时,入参个数、返回类型能够不合乎办法签名。

回调 Function 入参比签名少

fetchResults 有一个参数,即回调函数。该办法从某处获取数据,而后执行回调。回调的办法签名有两个参数,statusCoderesults

function fetchResults(callback: (statusCode: number, results: number[]) => void) {const results = [1,2,3];
  ...
  callback(200, results); 
}

咱们用上面的形式调用 fetchResults,留神办法签名是不同的,它没有第二个参数 results

function handler(statusCode: number) {
  // 业务解决
  ...
}

fetchResults(handler); // ✔️

能够失常编译,没有任何谬误或正告。看起来有点奇怪,但细想一下,你始终在这么用。

Array.prototype.forEach 办法签名

    /**
     * Performs the specified action for each element in an array.
     * @param callbackfn  A function that accepts up to three arguments. forEach calls the callbackfn function one time for each element in the array.
     * @param thisArg  An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
     */
    forEach(callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any): void

理论应用:

let items = [1, 2, 3];
items.forEach(arg => console.log(arg));

在运行时,forEach 应用三个参数 (value、index、array) 调用给定的回调函数,但大多数时候回调函数只应用其中的一个或两个参数。

那为什么不罗唆将 forEach 参数申明为可选。

forEach(callback: (element?: T, index?: number, array?: T[]))

如果申明为可选,因为回调的提供者不晓得调用方何时会传递多少参数,将不得不查看各个参数,这显然不是你想要的。

function maybeCallWithArg(callback: (x?: number) => void) {if (Math.random() > 0.5) {callback();
    } else {callback(42);
    }
}

申明非可选,是站在调用者的角度,保障按申明传递参数,能够兼容须要不同个数参数的回调函数;

这么解决的合理性在于,回调函数本身是最理解如何解决入参的,如果它不关怀某些入参,它能够平安的疏忽。

回调返回类型不匹配签名 return void

如果函数类型指定返回类型 void,则也承受具备不同的、更具体的返回类型的函数。同样,用后面的例子,这次减少 handle 的返回类型申明。

function handler(statusCode: number): {age:number}{
  //  业务解决
  ...
  return {"age": 4};
}

fetchResults(handler); // ✔️

fetchResults 承受的回调函数返回类型是 void,而这次的 handler 返回 {age:number} 类型,仍然失常编译。

你仍然能够将 callback 后果赋值给一个变量,但仅仅限于申明语句,其余操作都将编译失败。

function fetchResults(callback: (statusCode: number, results: number[]) => void) {const results = [1,2,3];
  ...
  let didItWork = callback(200, results); // ✔️  1)console.log(didItWork); // ✔️
  console.log(didItWork.age) // ❌
  didItWork = {"age": 4} ; // ❌
}

// 留神尽管编译报错,但不影响最初 js 执行,Playground 运行后果
[LOG]: {"age": 4} 
[LOG]: 4 

可能有人困惑既然 void 类型无奈进行其余操作,为什么要容许 1)处赋值 void 类型给变量。TypeScript 1.4 语言标准 给出了如下阐明:

留神:咱们思考过禁止申明 Void 类型的变量,因为它们没有用途。然而,因为容许将 Void 作为泛型类型或函数的类型参数,因而不容许 Void 属性或参数是不可行的。

function foo<T>(param:T) {let localParam:T = param;}
foo<number>(3); // ✔️
foo<void>(undefined); // ✔️

这么解决的合理性在于,回调函数的调用者通过申明 callback 返回 void, 它最分明也能够保障返回值不会被应用。

示例 Playground

退出移动版