在我们的代码库中,下面的代码无法检测到是否由于程序员错误而发生了type violation
-为什么会发生这种情况以及解决此问题的最佳方法是什么?
// forcing a structure on "shape" of the output
interface IPayloadOutput {
[key: string]: string
}
interface MyPayloadOutput extends IPayloadOutput {
myPayloadKey1: string
myPayloadKey2: string
extraKey: string //adding this here doesn't cause compile error if it's not returned
}
// Input data needs that to be transformed to the output
interface MyPayloadInput {
data1: string
data2: string
}
class MyPayloadOutputGenerator extends PayloadOutputGenerator {
public getPayloadKeyValues(args: MyPayloadInput): IPayloadKeyValues {
return {
myPayloadKey1: {key1: args.data1, key2: args.data2},
myPayloadKey2: { key1: args.data1 + '-senor' },
// Why does code not throw compile error if the below field is missing?
// extraKey: { key1: 'extra' }
}
}
}
function consumer(response: MyPayloadOutput): void {
console.log(response)
}
const x = new MyPayloadOutputGenerator().getPayloadOutput<MyPayloadInput, MyPayloadOutput>({
data1: 'hello',
data2: 'wrold',
})
consumer(x) // should throw compiler error if missing `extraString`
PayloadGenerator
接受Input
并将其转换为预期的Output
。但是返回的数据中缺少extraString
中的键MyPayloadOutput
,但是没有报告编译器错误吗?为什么会这样?
这是一个显示正在运行的示例的fiddle
为完整起见,这里是PayloadGenerator
:
// all payloads need key1 and an optional key2.
interface IPayloadDetails {
key1: string
key2?: string
}
// force structure for how we expect payload key/values to look
interface IPayloadKeyValues {
[key: string]: IPayloadDetails
}
abstract class PayloadOutputGenerator {
// source keys from implementing classes. `args` is not typed to be 'anything'
public abstract getPayloadKeyValues(args): IPayloadKeyValues
// Generic method to be used with any input/output combo
public getPayloadOutput<Input, Output>(args: Input): Output {
const payloadKeyValues = this.getPayloadKeyValues(args)
const payloadOutput = {} as Output
Object.keys(payloadKeyValues).forEach(key => {
payloadOutput[key] = JSON.stringify(payloadKeyValues[key]) //do custom encoding here
})
return payloadOutput
}
}
答案 0 :(得分:2)
您进行了打字(as Output
),并省略了中心位置(: IPayloadKeyValues
)的打字,并且总的来说,这与打字稿类型系统无关。通过使用索引类型,您可以使打字稿无法确定代码的实际作用。而是从一开始就使用泛型:
abstract class PayloadOutputGenerator<I, O extends {}> { // already introduce the generic here ...
public abstract getPayloadKeyValues(args: I): O // then you can narrow down this correctly
public getPayloadOutput(args: I) { // and this gets typed correctly automatically
const payloadKeyValues = this.getPayloadKeyValues(args);
const payloadOutput = {} as { [K in keyof O]: string }; // its a mapped type, so lets type it as such
Object.keys(payloadKeyValues).forEach(key => {
payloadOutput[key] = JSON.stringify(payloadKeyValues[key]) //do custom encoding here
});
return payloadOutput
}
}