如何在编译时捕获子类型到超类型的分配?

时间:2019-05-29 00:20:57

标签: typescript

learnt那个

  

TypeScript的结构类型系统的规则是,如果xy的成员至少相同,则yx兼容

这允许将子类型的变量分配给超类型的变量。有没有办法获得关于该分配的编译时错误?

TypeScript Playground

interface SuperT {
    field: string
}

// an explicitly declared subtype object for a supertype variable generates an error
const super1: SuperT = {field: 'value', extra: 1} // compile-time error: Type '{ field: string; extra: number; }' is not assignable to type 'SuperT'

function subTValue() { return {field: 'value', extra: 1} }
const super2: SuperT = subTValue() // no compile-time error, BUT HOW TO get a compile-time error here? 

2 个答案:

答案 0 :(得分:2)

您想要exact types不受直接支持。您可以执行various tricks with generics and conditional types来靠近。这是间接执行此操作的一种方法:

interface SuperT {
    field: string
}

type Exactly<T, U extends T> = T & Record<Exclude<keyof U, keyof T>, never>;
const asExactlySuperT = <U extends Exactly<SuperT, U>>(superT: U) => superT;

const superOkay: SuperT = asExactlySuperT({ field: "a" }); // okay

function subTValue() { return { field: 'value', extra: 1 } }
const superBad: SuperT = asExactlySuperT(subTValue()); // error! 
// types of property "extra" are incompatible

Link to code

这里的想法是Exactly<T, U>将使用类型T和候选类型U,希望它与T完全匹配,而没有额外的属性。如果是这样,则Exactly<T, U>将等于U。如果不是,则Exactly<T, U>会将所有其他属性的属性类型设置为never。由于asExactlySuperT<U>()要求U extends Exactly<SuperT, U>,因此唯一可能发生的方法是U中没有多余的属性。

希望有帮助。祝你好运!

答案 1 :(得分:0)

Ray Toal发现了一个非常相似的问题的答案here。 (我一开始就知道这一点,我只是想检查jcalz的反应时间。非常棒,@ jcalz!)

基于这种方法,我的代码如下:

TypeScript Playground

type StrictPropertyCheck<T, TExpected, TError> = Exclude<keyof T, keyof TExpected> extends never ? {} : TError

interface SuperT {
    field: string
}

function doIdentity<T extends SuperT>(a: T & StrictPropertyCheck<T, SuperT, "Only allowed properties of SuperT">) {
    return a
}


function subTValue() { return { field: 'value', extra: 1 } }

const super3: SuperT = doIdentity(subTValue()) // we do get a compile-time error!