typescript - Cum să creezi un tip, bazat pe proprietățile obiectelor dintr-un array?


Încerc să deduc tipul unui obiect de configurare, al cărui proprietăți sunt definite de un șir de descrieri de parametri.

Pe scurt:

// Based on the following array...
const params = [
    { id: "max", example: 75 },
    { id: "label", example: "My Label" },
];

// ...extract values from a request, creating a structure like...
const config = {
    max: 100,
    label: "Max Items",
};

// ... with type:
{ max: number, label: string }

Și asta e cât am ajuns cu codul meu.

// A simple parameter description interface
type ConfigParameter<IdType, ValueType> = Readonly<{
    // The `IdType` is necessary to get a stricter type
    // parameter instead of a generic `id: string;`. This will be needed
    // later when we infer the type of the `config` object.
    id: IdType;
    example: ValueType;
}>;

// Configuration parameters ------------------------------------

const max: ConfigParameter<"max", number> = {
    id: "max",
    example: 100,
};

const label: ConfigParameter<"label", string> = {
    id: "label",
    example: "My Label",
};

const configParams = [max, label] as const;

// Extracted configuration object ------------------------------
// At some point, the application uses the parameter descriptors
// above to extract data from a request. This is the implementation
// I have so far:

type Config<T extends Readonly<Array<ConfigParameter<string, any>>>> = {
    [key in T[number]["id"]]: T[number]["example"];
};

const config: Config<typeof configParams> = {
    max: 75,
    label: "Some index",
};

Acum, problema este că toate proprietățile din config au tipul number | string, în loc să aibă tipurile lor respective:

// Expected type
{ max: number, label: string }

// Actual type
{ max: number | string, label: number | string }

Înțeleg de ce obțin acest rezultat, dar nu am nicio idee cum să limitez tipul fiecărei chei individual.

Aveți sugestii?

Răspuns #1

Poftim:

type Config<T extends ReadonlyArray<ConfigParameter<string, any>>> = {
  [K in T[number]["id"]]: Extract<T[number], { id: K }>["example"]
};

Diferența aici este folosirea Extract, un tip utilitar care extrage părțile relevante din uniuni. Ar trebui să funcționeze așa cum ai nevoie. Poți să-l faci chiar mai atractiv (ieșirea, nu definiția) cu asta:

type Config<T extends ReadonlyArray<ConfigParameter<string, any>>> = {
  [K in T[number]["id"]]: Extract<T[number], { id: K }>["example"]
} extends infer O
  ? { [P in keyof O]: O[P] }
  : never;

Acum obții tiparea strictă pe care o dorești:

const config: Config<typeof configParams> = {
  max: 75,
  label: "Some index"
};
/* const config: {
    max: number;
    label: string;
} */

Ok, sper că asta ajută; mult noroc!

Link către cod

Răspunsul #2

încearcă să folosești interface:

interface ConfigParameter{
    max: number,
    label: string
}

const obj: ConfigParameter={ max: 75, label: "Un anumit index" };