Nelson Luk
Nelson Luk

Reputation: 311

How to create an object type based on keys/values of a const object in TypeScript?

I need to define a type based on a config object (anObject), which will be used in my code.

I wrote something like the following,

const anObject = {
  foo: ["a", "b", "c"],
  bar: ["d", "e", "f"],
} as const;

type aType =
  | { key: ""; value: [] }
  | { key: "foo"; value: ("a" | "b" | "c")[] }
  | { key: "bar"; value: ("d" | "e" | "f")[] };

const a: aType = { key: "", value: [] }; // No error
const b: aType = { key: "foo"; value: ["a", "b"] }; // No error
const c: aType = { key: "bar"; value: ["e", "f"] }; // No error

How to declare aType dynamically in TypeScript?

Upvotes: 2

Views: 1598

Answers (2)

BierDav
BierDav

Reputation: 1404

What about this code?

const anObject = {
    foo: ["a", "b", "c"],
    bar: ["d", "e", "f"],
} as const;

type aType = {
    key: keyof typeof anObject | "";
    value: typeof anObject[keyof typeof anObject][number][];
} & (
    | { key: ""; value: undefined[] }
    | { key: "foo"; value: typeof anObject["foo"][number][] }
    | { key: "bar"; value: typeof anObject["bar"][number][] }
    );

const foo: aType = { key: "", value: [] };

I simply added the undefined type, so that I don't get an TypeScript error. Solves that your problem?

And the second problem is a simple implementation error, because if you base your type on the config object sure your type must have all properties of it. So you have to change your approach. If you can explain your needs more detailed I may be able to help you.

I know that this is very far-fetched now, but can it be that you wanted to something like this:

export interface MyConfiguration {
    foo?: ("a" | "b" | "c")[],
    bar?: ("d" | "e" | "f")[],
}

const DefaultMyConfiguration: MyConfiguration = {
    foo: ["b", "c"],
    bar: ["d"]
}

//In the middle of somewhere in your code you can now do this
let config: MyConfiguration
function myConstructor(config: MyConfiguration) {
    config = {...DefaultMyConfiguration, ...config}
}

Upvotes: 0

It worth using union here:

const anObject = {
    foo: ["a", "b", "c"],
    bar: ["d", "e", "f"],
} as const;

type AnObject = typeof anObject;

type Values<T> = T[keyof T]

type AType = {
    [P in keyof AnObject]: {
        key: P,
        value: Array<AnObject[P][number]>
    }
}

type Default = { key: ""; value: [] }

type Result = Values<AType> | Default

const foo: Result = { key: "", value: [] }; // ok
const bar: Result = { key: "bar", value: ["d"] } // ok

Feel free to add any new keys to anObject. You don't need to make updates anywhere else

Please keep in mind, as @T.J Crowder said, there is no order in value property. TS will allow different combinations of value: [d,e] | [e, d] | [d].

If you want it in same order, just use this line: value: AnObject[P] in AType helper

Upvotes: 3

Related Questions