Making arrays of generic interfaces

Viewed 86

Let's say we have the following interface:

interface I<A extends string, B extends string> {
    a: A,
    b: B,
    c: `${A} ${B}`
}

I want to make a function which makes an array of I and validates its types so that

makeArr([
    {
        a: "1",
        b: "2",
        c: <- it should have type "1 2" | "3 4"
    },
    {
        a: "3",
        b: "4",
        c: <- it should have type "1 2" | "3 4"
    }
])

I have already tried implementing it like this:

type Arr<A extends string, B extends string> = I<A, B>[]

function makeArr<A extends string, B extends string>(arr: Arr<A, B>) {
    return arr
}

However, the c property still accepts any string

makeArr([
    {
        a: "1",
        b: "2",
        c: "this does not work"
    },
    {
        a: "3",
        b: "4",
        c: "whatever "
    }
])

It does a little bit better when you specify the types explicitly, but c accepts some wrong types anyway

makeArr<"1" | "3", "2" | "4">([
    {
        a: "1",
        b: "2",
        c: "3 2"
    },
    {
        a: "3",
        b: "4",
        c: "1 4"
    }
])

I also wouldn't like to specify it explicitly, because those arrays may be very long

Edit: Thanks to aleksxor's answer I managed to implement it, however the types must be specified explicitly Playground Link

1 Answers

I believe there must be a more elegant way. But here is my attempt to implement such a check:

type I<T extends { a: string, b: string }> = T extends {
    a: T['a'],
    b: T['b'],
    c: `${T['a']} ${T['b']}`
} ? T : never

function makeArr<T extends { a: string, b: string }>(arr: readonly [...I<T>[]]) {
    return arr
}

makeArr([
    {
        a: "1",
        b: "2",
        c: "doesn't pass"
    },
    {
        a: "3",
        b: "4",
        c: "3 4"
    }
])

playground link

Related