5

Hello I want to create a type for an object like this:

const z = {
    name: { // this one is special
        buty: ['qqqq']
    },
    lipa: ['xxx'],
    // more keys here
};

Bacially it is an object like this

type Test = {
    [key: string]: string[]
}

with one small exception. It always has a key name with a little bit different value.

type Special = {
    name: {
        [key: string]: string[]
    }
}

but when I trying to merge these two types

type Test =
    { [key: string]: string[] } &
    { name: { [key: string]: string[] } };

const z: Test = {
    name: { // this one is special
        buty: ['qqqq']
    },
    lipa: ['xxx'],
    // more keys here
};

I get an error Type '{ buty: string[]; }' is missing the following properties from type 'string[]': length, pop, push, concat, and 26 more.(2322).

Is it possible to create a type for an object like this?

TypeScript Playgroud

Konrad Klimczak
  • 1,474
  • 2
  • 22
  • 44
  • @T.J. Crowder I don't think it is the same question, where the original works on specific interface, where this one uses general string key. This should be reopened, as I believe nobody will solve this one by using the one you have linked. – Maciej Sikora Apr 22 '21 at 10:39

1 Answers1

7

Below solution based on mapped types:

type Test<T extends  { [key: string]: any }> =
    {
        [K in keyof T]:
            K extends 'name' ? { [key: string]: string[] } : string[]
    }

// id function to make type more accurate by generic
const makeTest = <T extends  { [key: string]: any }>(obj: Test<T>): Test<T> => obj
   

// correct use
const z = makeTest({
    name: { // this one is special
        buty: ['qqqq']
    },
    lipa: ['xxx'],
    // more keys here
});

// error as object wrong
const y = makeTest({
    name: ['xxx'], // no it is special
    lipa: ['xxx'],
    // more keys here
});

We can achieve the need by using id function (x => x) which will narrow the type thanks to use of generic type. TS has better narrowing when we use generics, and exactly this function allows us for that. Also the type you have made was not working because string | 'name' key evaluates into string therefore the second part of intersection was totally omitted.

The solution is to make the type conditional and for special 'name' key set different value type.

playground

Maciej Sikora
  • 19,374
  • 4
  • 49
  • 50