Texto datilografado: derivar tipo de união da matriz de objetos

8

Gostaria de declarar uma matriz de itens aplicada por tipo e poder derivar um tipo de união a partir dela. Esse padrão funciona se você não atribuir explicitamente um tipo aos itens na matriz. Não tenho certeza de como melhor explicá-lo, então aqui está um exemplo:

EXEMPLO 1

type Pair = {
  key: string;
  value: number;
};

const pairs: ReadonlyArray<Pair> = [
  { key: 'foo', value: 1 },
  { key: 'bar', value: 2 },
] as const;

type Keys = typeof pairs[number]['key']

EXEMPLO 2

type Data = {
  name: string;
  age: number;
};

const DataRecord: Record<string, Data> = {
  foo: { name: 'Mark', age: 35 },
  bar: { name: 'Jeff', age: 56 },
} as const;

type Keys = keyof typeof DataRecord;

Aqui está um exemplo de derivação das chaves ao usar as const. Eu quero esse mesmo comportamento, mas com a matriz sendo explicitamente digitada.

const pairs = [
  { key: 'foo', value: 1 },
  { key: 'bar', value: 2 },
] as const;

type Keys = typeof pairs[number]['key']; // "foo" | "bar"

valor desejado das chaves: "foo"|"bar"

valor real das chaves: string

Ben
fonte
4
Eu não acho que você pode fazer isso dinamicamente da maneira que está tentando, está confundindo valores de tempo de execução com tipos de tempo de compilação. Você terá que atribuir ao keyatributo do Pairtipo o tipo que deseja, e ele deve funcionar como você o escreveu.
Jared Smith
4
Isso responde sua pergunta? Typecript derivar tipo de união a partir de valores de tupla / matriz
vicpermir 02/03
@ Jaredmith, isso não deve ser um problema em tempo de execução. Estou usando isso para declarar um número arbitrário de valores que não mudam durante a execução. Isso seria equivalente à chave de configuração: "foo" | "bar" na declaração de tipo.
Ben
"isto não deve ser um problema em tempo de execução" --- typescript não possui tempo de execução, portanto, é um problema fazê-lo em tempo de execução.
zerkms 2/03
1
@ Ben me permita ser mais específico: eu não acho que você pode fazer isso com uma tupla de propriedades extraídas de tipos de referência mutáveis ​​da maneira que você pode com uma tupla de primitivas imutáveis. Você pode dizer, possibleKeys = ['foo', 'bar'] as const; type Keys = typeof possibleKeys[number]; type Pair = { key: Keys, value: number };mas ainda precisa enumerar explicitamente as chaves possíveis.
Jared Smith

Respostas:

3

Para uma variável, você pode deixar o compilador inferir o tipo da inicialização ou gravá-lo explicitamente. Se você o escrever explicitamente, como você fez, o valor de inicialização será verificado na anotação, mas o tipo real do inicializador não afeta o tipo da variável (para que você perca as informações de tipo que deseja). Se você deixar o compilador inferir, não será mais possível restringir o tipo a uma interface específica (como você deseja)

A solução para isso é usar uma função genérica para restringir o valor e inferir que é do tipo real:

type Pair = {
  key: string;
  value: number;
};
function craetePairsArray<T extends readonly Pair[] & Array<{key: V}>, V extends string>(...args: T) {
    return args
}

const pairs = craetePairsArray(
  { key: 'foo', value: 1 },
  { key: 'bar', value: 2 },
)

type Keys1 = typeof pairs[number]['key']

type Data = {
  name: string;
  age: number;
};

function craeteDataObject<T extends Record<string, Data>>(arg: T) {
    return arg;
}
const DataRecord = craeteDataObject({
  foo: { name: 'Mark', age: 35 },
  bar: { name: 'Jeff', age: 56 },
})

type Keys2 = keyof typeof DataRecord;

Link para parque infantil

Nota: Para o caso do array, precisamos armar um pouco o compilador para inferir tipos literais de strings para key, portanto, o todo & Array<{key: V}>, onde Vestá um parâmetro de tipo que se estendestring

Titian Cernicova-Dragomir
fonte
1
Obrigado! Isso é exatamente o que eu precisava!
Ben
2

As abordagens usuais são:

  • deixe TS inferir o tipo de pairsomitindo o tipo explícito ReadonlyArray<Pair>(consulte a resposta )
  • dar no tipokeyPair"foo"|"bar"

Se você não quiser fazer isso, a única maneira de inferir suas chaves e restringir o tipo pairsé usar uma função auxiliar. O Pairtipo também será tornado genérico para salvar os keytipos literais de string fornecidos . Você pode usar um IIFE para compactar a tarefa:

type Pair<K = string> = {
    key: K;
    value: number;
};

const pairs = (<T>(p: readonly Pair<T>[]) => p)([
    { key: 'foo', value: 1 },
    { key: 'bar', value: 2 },
] as const) // readonly Pair<"foo" | "bar">[]

type Keys = typeof pairs[number]['key'] // "foo" | "bar"

Parque infantil

bela53
fonte