I have an interface and want to determine if any of the values are optional. To do that, I thought I'd create a union out of all the values and then check if it extends undefined
. When it didn't work, I read about distributive conditionals and thought surely that would be the solution.
Here's the code:
export interface SomeInterface {
someBool: boolean
someOptionalNum?: number
someOptionalText?: string
}
const test: [SomeInterface[keyof SomeInterface]] extends [undefined] ? boolean : string = true
According to this part of the typescript guide, I would expect this code to work. But I can't get test
to ever be a boolean
, it remains stuck on string
.
Our understanding of the way extends works is causing the issue here.
Conditional types take a form that looks a little like conditional expressions (condition ? trueExpression : falseExpression) in JavaScript:
SomeType extends OtherType ? TrueType : FalseType;
When the type on the left of the extends is assignable to the one on the right, then you’ll get the type in the first branch (the “true” branch); otherwise you’ll get the type in the latter branch (the “false” branch).
Take a look at this code for example:
type X = "A" | "B";
type A = "A";
type Y = A extends X ? true : false;
Y would be true above.
In the question, [SomeInterface[keyof SomeInterface]]
is not assignable to [undefined]
. In fact, [undefined]
is assignable to [SomeInterface[keyof SomeInterface]]
.
So you have the conditions the other way round. You can change your code like:
export interface SomeInterface {
someBool: boolean
someOptionalNum?: number
someOptionalText?: string
}
const test: [undefined] extends [SomeInterface[keyof SomeInterface]] ? boolean : string = true
and it should work.