首页 > 解决方案 > 为什么 TypeScript 在编译时无法检测到不匹配的类型以及如何修复它?

问题描述

在我们的代码库中,我们有如下代码,它无法检测是否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。但是返回的数据中缺少key extraStringinMyPayloadOutput却没有报编译错误?这是为什么?

这是一个显示正在运行的示例的小提琴

为了完整起见,这里是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
    }

}

标签: javascripttypescript

解决方案


您在中心位置()进行了类型转换(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
  }
}

推荐阅读