类型防护适用于错误类型但接受未知类型

时间:2018-05-15 22:25:36

标签: typescript

我有以下“视觉测试”(又名,我只是使用vs-code来直观地检查是否存在之下的红色波浪线)。它的目的是确保我传入的泛型类型确实将被调用约束为substitute()

interface IJob {
  jobId: string;
  extraneous?: number;
}

/**
 * Bear in mind these tests are just to visually identify in
 * the editor that the generics are passing their types around
 * correctly.
 */
describe("Typings → ", () => {

  it.skip('setting <T> imposes structure on the substitutions that are allowed in', () => {
    const wrongType = {
      jobId: "1234",
      extraneous: "foobar"
    }
    const nonExistantType = {
      jobId: "1234",
      noCanDo: true
    }
    const ruleAbidingJob = {
      jobId: "1234"
    }
    const t = TypedTemplate.create<IJob>();
    t.substitute(wrongType); // this should be failing in TS
    t.substitute(nonExistantType); // this should be failing in TS
    t.substitute(ruleAbidingJob); // this should be fine
  });
});

似乎允许ruleAbidingJob并正确识别wrongType已将无关设置为字符串而非数字作为接口定义它。不幸的是,它允许使用nonExistantType而不会出错。

如果有帮助,这是视觉效果:

enter image description here

还有一个屏幕截图,显示substitute的预期类型确实是 IJob

enter image description here

1 个答案:

答案 0 :(得分:1)

Typescript将包含额外属性的类型(nonExistantType的类型)视为IJob的子类型,这就是允许调用的原因。您可以将函数参数类型视为函数需要工作的最小属性集,因此如果传入的参数具有更多属性,则函数应该不关心。

Typescript确实有一个feature,它将额外的属性标记为错误,但仅当对象文字直接分配给特定类型的变量/参数时。在您的情况下,您可以将对象文字直接传递给函数,也可以显式键入常量:

const nonExistantType :IJob = { // Error
    jobId: "1234",
    noCanDo: true
} 
    // OR
t.substitute({
    jobId: "1234",
    noCanDo: true // error
})

修改

我们还可以通过在typescript 2.8中使用条件类型来检查没有额外的属性,创建对象:

const wrongType = {
    jobId: "1234",
    extraneous: "foobar"
}
const nonExistantType = {
    jobId: "1234",
    noCanDo: true
}
const ruleAbidingJob = {
    jobId: "1234"
}

type NoExtraProperties<TSource, TTarget> = Exclude<keyof TSource, keyof TTarget> extends never ? true : "Extra properties detected";
function ensueNothingExtra<TSource, TTarget>(source : TSource, validation: NoExtraProperties<TSource, TTarget>) :TTarget {
    return source as any
}
const t = TypedTemplate.create<IJob>();
t.substitute(ensueNothingExtra(wrongType, true)); // this is an error
t.substitute(ensueNothingExtra(nonExistantType, true)); // this is also an error
t.substitute(ensueNothingExtra(ruleAbidingJob, true)); // this is ok