admin管理员组文章数量:1318582
I have a discriminated union (do I?) with discriminating property x
of types number | undefined
and number
. I thought checking for x === undefined
would suffice to narrow type down, but it doesn't work (typescript 5.7.3). Surprisingly it works if instead of number
I use any literal type or even literal type union.
const assertIsOption1 = (option1: 'option1') => undefined;
type CreateUnionType<T> = (
{ x: T | undefined, y: 'option1'} |
{ x: T, y: 'option2' }
)
// Doesn't work with "broader types":
type UnionWithBroadTypeInDiscriminatoryKey = CreateUnionType<number>
const obj = { x: undefined, y: 'option1' } as UnionWithBroadTypeInDiscriminatoryKey;
if (obj.x === undefined) {
assertIsOption1(obj.y)
}
// does work with literals:
type UnionWithLiteralTypeInDiscriminatoryKey = CreateUnionType<5>
const obj2 = { x: undefined, y: 'option1' } as UnionWithLiteralTypeInDiscriminatoryKey;
if (obj2.x === undefined) {
assertIsOption1(obj2.y)
}
// and does work with union of literals:
type UnionWithLiteralTypeInDiscriminatoryKey2 = CreateUnionType<5 | 6 | 'some string literal'>
const obj3 = { x: undefined, y: 'option1' } as UnionWithLiteralTypeInDiscriminatoryKey2;
if (obj3.x === undefined) {
assertIsOption1(obj3.y)
}
Click here to view and run this code on TypeScript Playground
I have a discriminated union (do I?) with discriminating property x
of types number | undefined
and number
. I thought checking for x === undefined
would suffice to narrow type down, but it doesn't work (typescript 5.7.3). Surprisingly it works if instead of number
I use any literal type or even literal type union.
const assertIsOption1 = (option1: 'option1') => undefined;
type CreateUnionType<T> = (
{ x: T | undefined, y: 'option1'} |
{ x: T, y: 'option2' }
)
// Doesn't work with "broader types":
type UnionWithBroadTypeInDiscriminatoryKey = CreateUnionType<number>
const obj = { x: undefined, y: 'option1' } as UnionWithBroadTypeInDiscriminatoryKey;
if (obj.x === undefined) {
assertIsOption1(obj.y)
}
// does work with literals:
type UnionWithLiteralTypeInDiscriminatoryKey = CreateUnionType<5>
const obj2 = { x: undefined, y: 'option1' } as UnionWithLiteralTypeInDiscriminatoryKey;
if (obj2.x === undefined) {
assertIsOption1(obj2.y)
}
// and does work with union of literals:
type UnionWithLiteralTypeInDiscriminatoryKey2 = CreateUnionType<5 | 6 | 'some string literal'>
const obj3 = { x: undefined, y: 'option1' } as UnionWithLiteralTypeInDiscriminatoryKey2;
if (obj3.x === undefined) {
assertIsOption1(obj3.y)
}
Click here to view and run this code on TypeScript Playground
Share Improve this question edited Jan 21 at 11:43 Rafael 2,0492 gold badges26 silver badges63 bronze badges asked Jan 21 at 11:11 yjayyjay 1,0235 silver badges14 bronze badges 3 |1 Answer
Reset to default 2Discriminated unions need to have a valid discriminant property that is a unit/literal type of a union of such types. The type undefined
counts as a discriminant by itself, but neither the wide type number
nor number | undefined
counts, so you can't check your type's x
property to discriminate the union. (Note that your type is a discriminated union with y
being a valid discriminant, but for some reason you aren't trying to check that property.) This restriction could be considered a design limitation, as described at microsoft/TypeScript#30506; allowing wider discriminants is not part of the language, and judging from the issues linked within that issue, it doesn't look like it will become one soon (there is an implementation at microsoft/TypSeScript#60718 but apparently it breaks lots of real world code).
There is some support for non-literal discriminants where some members of the union have wider type, but I believe at least one member of the union needs to be a "true" discriminant property. So if one union member has undefined
as the discriminant, then other members can have wide types like number
. This leads to the following refactoring of your types:
type CreateUnionType<T> = (
{ x: undefined, y: 'option1' } |
{ x: T, y: 'option1' | 'option2' }
)
which works as intended:
const obj = { x: undefined, y: 'option1' } as UnionWithBroadTypeInDiscriminatoryKey;
if (obj.x === undefined) {
assertIsOption1(obj.y); // okay
}
With the example code it does seem like you probably want to keep your original type and discriminate based on y
to start with, but I guess that's out of scope for the question as asked.
Playground link to code
本文标签: typescriptDiscriminated union type with discriminating property as unionStack Overflow
版权声明:本文标题:typescript - Discriminated union type with discriminating property as union - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1742047012a2417845.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
const obj = { x: undefined, y: 'option1' } as UnionWithBroadTypeInDiscriminatoryKey;
you writeconst obj: UnionWithBroadTypeInDiscriminatoryKey = { x: undefined, y: 'option1' };
it works as expected, but its strange, in both cases the type ofobj
isUnionWithBroadTypeInDiscriminatoryKey
, so AFAIK in both cases should work exactly the same – Daniel Cruz Commented Jan 21 at 13:21number
in there; discriminants need to be literal types. Neithernumber
nornumber | undefined
is a literal type. If you want to refactor to this version thenundefined
is a literal type and can be used to discriminate. Does that fully address your question? If so I'll write an answer or find a duplicate. If not, what's missing? – jcalz Commented Jan 21 at 14:17