1

Conditional type with generic and object works fine, for example:

interface Bar<T extends Record<string, any>> {
    x: T extends { x: number } ? number : string,
    y: T extends { y: number } ? number : string,
    z: T extends { z: number } ? number : string,
}

const bar: Bar<{x: 1, y: 2, z: ""}> = {
    x: 1,
    y: 2,
    z: 3 // expected error, z supposed to be a string
}

How can I apply the same principle with generic type of array? That said, how can I check if the generic includes a value or not? I want something like

interface Foo<T extends number[]> {
    x: T includes 1 ? number : string,
    y: T includes 2 ? number : string,
    z: T includes 3 ? number : string,
}

const foo: Foo<[1,2]> = {
    x: 1,
    y: 2,
    z: ""
}

But obviously T includes x is not a valid TypeScript syntax. Is this even possible?

I already tried with:

x: T includes 1 ? number : string,
x: T includes [1] ? number : string,
x: T includes Array<1> ? number : string,

None of them works

Playground example

I can get close to what I want by checking for specific values at specific indexes of the type argument using T[0] extends 1 and similar:

interface Foo<T extends number[]> {
    x: T[0] extends 1 ? number : string,
    y: T[1] extends 2 ? number : string,
    z: T[2] extends 3 ? number : string,
}

const foo: Foo<[1,2]> = {
    x: 1,
    y: 2,
    z: 3 // expected error, the type argument doesn't have a number at index 2, so `z` is type string
}

const foo2: Foo<[1,2,4]> = {
    x: 1,
    y: 2,
    z: 3 // expected error, the type argument has 4 at index 2, not 3, so `z` is type string
}

Playground example

But that's order-dependent. Can I do that without having it be order-dependent? So z must be number only if the type argument has 3 anywhere in it, and string otherwise?

6
  • I'm afraid I'm really struggling to figure out what you're trying to do. The situations don't seem similar. In your object example, the different properties can have different types, but in your array example, T is extends number[], so the entries in T have to be numbers. When would you expect to let x, y, or z be strings instead? Commented Mar 27, 2021 at 11:59
  • If you're trying to see if the generic argument [1, 2] contains the value 1 anywhere in it, I'm 99% sure you can't do that in the type system. You could test individual elements of the type parameter, but order would be important. Commented Mar 27, 2021 at 12:00
  • 2
    Why are you using tuple types instead of just unions like 1 | 2? Commented Mar 27, 2021 at 12:19
  • 1
    I've deleted my answer and folded it into the question. If you don't like my change, please feel free to just roll it back. Copying answers into the question is not something the person asking the question should do, but it's fine if the person posting the answer does it -- provided it's okay with the person posting the question, too. :-) I did it because it seemed like it helped the question along, and I knew I wasn't going to be able to take a solution further. Commented Mar 27, 2021 at 12:20
  • @jcalz - Why didn't I think of that?! (Don't answer that, we both know why. :-) ) Like this? Commented Mar 27, 2021 at 12:22

1 Answer 1

4

As jcalz pointed out (I wouldn't have thought of it, I'm not good enough in TypeScript yet), you can do this with a union type rather than with a tuple:

interface Foo<T extends number> {
    x: T extends 1 ? number : string,
    y: T extends 2 ? number : string,
    z: T extends 3 ? number : string,
}

const foo: Foo<1|2> = {
    x: 1,
    y: 2,
    z: 3 // expected error, the type argument doesn't have 3
}

const foo2: Foo<2|1|3> = {
    x: 1,
    y: 2,
    z: 3 // no error, the type argument has as 3 -- and the order doesn't matter
}

const foo3: Foo<2|1|"three"> = { // error, "three" doesn't extend number
    x: 1,
    y: 2,
    z: 3
}

Playground link

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.