I am trying to use an object and its items as type.
In a code
const obj = {
a: [
'hi',
'hello'
] as const,
b: [
'see you',
'good bye'
] as const
}
type Level1 = keyof typeof obj;
type Level2 = typeof obj[Level1][number];
type Levels = [Level1, Level2];
const a: Levels = ['a', 'hi']
What I want is that Level2 becomes 'hi'|'hello' when Level1 is 'a' and 'see you'|'good bye' when Level1 is 'b'. However, Level2 becomes 'hi'|'hello'|'see you'|'good bye', whatever Level1 is. Thus
const a: Levels = ['a', 'hi']; // it should work
const b: Levels = ['a', 'see you'] // it shouldn't work because a does not have 'see you'.
How should Level2 be defined to perform?
CodePudding user response:
Make the Levels
type generic so that it can narrow down which second item in the tuple is permitted, given the first item.
type Obj = typeof obj;
type Levels<T extends keyof Obj> = [T, Obj[T][number]];
const a: Levels<'a'> = ['a', 'hi']
Less repetitively, use a function to verify the type so you don't have to type 'a'
twice.
type Obj = typeof obj;
const getA = <T extends keyof Obj>(arr: [T, Obj[T][number]]) => arr;
const a = getA(['a', 'hi']);
CodePudding user response:
Slightly different answer that allows you to use Levels
as in the OP:
type Level1 = keyof typeof obj;
type Levels<T = Level1> = T extends Level1 ? [T, (typeof obj)[T][number]] : never;
const a: Levels = ['a', 'hi'];
This makes use of distributive conditional types, which is why there needs to be an extends
on the right of the equal sign rather than as a constraint on T
.