TypeScript泛型类型检查在传入枚举时更改类型的行为

nzkunb0c  于 2022-12-27  发布在  TypeScript
关注(0)|答案(1)|浏览(128)

我遇到了以下问题:

type NonUndefined<T> = T extends undefined ? never : T;

enum TestEnum {
    T1 = "T1",
    T2 = "T2",
}

interface Data {
    Val: TestEnum;
}

type AltFormData<T> = T extends object
    ? {
          readonly [P in keyof T]-?: AltFormData<NonUndefined<T[P]>>;
      } & AltFormValue<NonUndefined<T>>
    : AltFormValue<NonUndefined<T>>;

type Alt1FormData<T> = AltFormValue<NonUndefined<T>>;

type Alt2FormData<T> = T extends object ? AltFormValue<NonUndefined<T>> : AltFormValue<NonUndefined<T>>;

type AltFormValue<T = any> = {
    readonly $setValue: (value: T | undefined) => void;
};

const x2: AltFormData<Data> = {};
x2.Val.$setValue(TestEnum.T1); // ERROR - x2.Val is of type AltFormValue<TestEnum.T1> | AltFormValue<TestEnum.T2> and should be of type AltFormValue<TestEnum>

const x3: AltFormValue<TestEnum> = {};
x3.$setValue(TestEnum.T1); // GOOD

const x4: AltFormData<TestEnum> = {};
x4.$setValue(TestEnum.T1); // ERROR - x4 is of type AltFormValue<TestEnum.T1> | AltFormValue<TestEnum.T2> and should be of type AltFormValue<TestEnum>

const x5: AltFormValue<NonUndefined<TestEnum>> = {};
x5.$setValue(TestEnum.T1); // GOOD

const x6: Alt1FormData<TestEnum> = {};
x6.$setValue(TestEnum.T1); // GOOD

const x7: Alt2FormData<TestEnum> = {};
x7.$setValue(TestEnum.T1); // ERROR - x7 is of type AltFormValue<TestEnum.T1> | AltFormValue<TestEnum.T2> and should be of type AltFormValue<TestEnum>

似乎类型检查T extends object改变了结果。同样的事情发生在使用类型"T1" | "T2"而不是枚举时。您可以将此代码复制并粘贴到TS Playground中,或者只需单击链接。

3duebb1j

3duebb1j1#

看起来typescript把enum类型当作它的值的并集,这对条件类型有影响。
所以,当你有这种类型:

type Alt2FormData<T> = T extends object ? AltFormValue<NonUndefined<T>> : AltFormValue<NonUndefined<T>>;

然后你给它一个枚举,实际上是TestEnum.T1 | TestEnum.T2,typescript会计算类型。

AltFormValue<NonUndefined<TestEnum.T1>> | AltFormValue<NonUndefined<TestEnum.T2>>

这就是Distributive conditional types,这是typescript默认处理条件中联合类型的方式,如果你不想这样,你需要关闭这个选项,把Tobject放到括号里.

type Alt2FormData<T> = [T] extends [object] ? AltFormValue<NonUndefined<T>> : AltFormValue<NonUndefined<T>>;

这将导致改为计算此类型:

AltFormValue<NonUndefined<TestEnum.T1 | TestEnum.T2>>

如果进行简化,则这就是所需的类型

AltFormValue<NonUndefined<TestEnum>>

其别名为:

{
    readonly $setValue: (value: TestEnum | undefined) => void;
}

那么您就不会看到编译错误。

相关问题