¿Cómo asignar propiedades dinámicamente a un objeto en TypeScript utilizando Record correctamente?
Intento definir un tipo que pueda asignar cualquier clave en un objeto. Luego, encontré que esta es una buena respuesta.
type AnyMap = Record<string, any>;
const obj: AnyMap = {};
const obj1: AnyMap = {
a: 'a',
b: 'b'
}
Sin embargo, encontré que AnyMap también se podía asignar a un arreglo y no muestra ningún error.
const arr1: AnyMap = ['a', 'b'];
Solo quiero que AnyMap defina únicamente el tipo de objeto, sin incluir arreglo. Entonces, intento crear otro tipo.
type UnknownMap = Record<string, unknown>;
// obj2 is works fine
const obj2: UnknownMap = {
a: 'a',
b: 'b'
}
// obj3 is works fine
const obj3: UnknownMap = {
0: 'a',
1: 'b'
}
// arr2 throws an error
// Type 'string[]' is not assignable to type 'UnknownMap'. Index signature is missing in type 'string[]'.(2322)
const arr2: UnknownMap = ['a', 'b'];
El resultado de UnknownMap me parece bueno. Solo define el tipo de object y lanza un error de array. Finalmente, tengo dos preguntas que no entiendo del todo.
Si solo quiero definir el tipo de objeto y no quiero incluir el tipo de array. ¿Es mejor usar
UnknownMapen lugar deAnyMap?¿Por qué
const arr2: UnknownMap=['a', 'b']lanza el errorType 'string[]' is not assignable to type 'UnknownMap'. Index signature is missing in type 'string[]'.(2322)? No sé por qué lanza un error solo porque reemplacéanyporunknownenRecord. Además, no sé por quéobj3funciona bien peroarr2lanza un error.
Área de juego de TypeScript
Espero que el enlace de arriba pueda ayudar a explicar bien mi pregunta. Si hay alguna descripción que te cause confusión, por favor házmelo saber, gracias.
Respuesta #1
Parte 1
¿Por qué const arr2: UnknownMap=['a', 'b'] arroja el error Type 'string[]' is not assignable to type 'UnknownMap'. Index signature is missing in type 'string[]'.(2322)? No sé por qué da un error solo porque reemplacé any con unknown en Record. Además, no sé por qué obj3 funciona bien, pero arr2 arroja un error.
Para entender mejor este error, por favor cambie a la versión 4.4.0 de TypeScript en el TS Playground.
Ya que hubo mejoras en los mensajes de error.
Creo que este error (de TS 4.3.*): Type 'string[]' is not assignable to type 'UnknownMap'. Index signature is missing in type 'string[]' no es correcto porque string[] tiene firma de índice.
Vea lib.es5.d.ts. Esta es una parte de la declaración del tipo Array:
// lib.es5.d.ts
interface Array<T> {
[n: number]: T
}
Si cambias a TS 4.4.0 - beta en el playground de TS, verás este error:
Type 'string[]' is not assignable to type 'UnknownMap'.
Index signature for type 'string' is missing in type 'string[]'
El mensaje de error anterior tiene sentido.
Para entender mejor el mensaje de error de TS, considere el siguiente ejemplo:
declare var arr: { [prop: number]: string };
declare var rec: { [prop: string]: unknown };
arr = rec; // error
rec = arr // ok
Mensaje de error:
Type '{ [prop: string]: unknown; }' is not assignable to type '{ [prop: number]: string; }'.
'string' and 'number' index signatures are incompatible.
Type 'unknown' is not assignable to type 'string'.(2322)
Por lo tanto, podemos reducir nuestro problema a esto:
declare var str: string;
declare var unk: unknown;
str = unk;
unk = str
Entonces, el problema principal aquí, que Type 'unknown' is not assignable to type 'string', es si any se puede asignar al tipo string.
Volvamos a nuestro mensaje de error principal:
type AnyMap = Record<string, any>;
const obj1: AnyMap = {
a: 'a',
b: 'b'
}
const arr1: AnyMap = ['a', 'b'];
type UnknownMap = Record<string, unknown>;
const obj2: UnknownMap = {
a: 'a',
b: 'b'
}
const obj3: UnknownMap = {
0: 'a',
1: 'b'
}
const arr2: UnknownMap = ['a', 'b']; // error
Mensaje :
Type 'string[]' is not assignable to type 'UnknownMap'.
Index signature for type 'string' is missing in type 'string[]'
Dado que Record<string, unknown> es solo un alias de
{
[x: string]: unknown;
}
y ['a', 'b'] inferido a string[], o en otras palabras a
{
[x: number]: string;
}
la firma de índice para [x: number]: string; no se puede asignar a [x: string]: unknown, porque El tipo 'unknown' no se puede asignar al tipo 'string'
Ten en cuenta que x:string como firma de índice en sí mismo es asignable a x:number y viceversa, porque según la especificación de JS, todas las claves se evalúan como string.
Parte 2
Si solo quiero definir el tipo de objeto y no quiero incluir el tipo de arreglo. ¿Es mejor usar UnknownMap en lugar de AnyMap?
Para distinguir entre diccionario y arreglo, necesitas inferir este tipo desde algún lugar. Puedes crear un tipo de utilidad para tal propósito o inferirlo a partir de los argumentos de una función. Ten en cuenta que no existe un operador negation en TypeScript.
Considera este ejemplo:
type Primitives =
| string
| number
| symbol
| boolean
| undefined
| bigint
| null // not sure how you want to treat this one :D
type AnObject<T = any> =
T extends Primitives | Array<any> ? never : T
{
type Test = AnObject<Record<number, 2>> // ok
type Test2 = AnObject<[]> // never
type Test3 = AnObject<2> // never
}
const onlyObj = <T,>(obj: AnObject<T>) => { }
onlyObj([]) // Error
onlyObj(2) // Error
onlyObj({}) // ok
Aquí, en mi blog, puedes encontrar más ejemplos/información sobre la negación de tipos en TypeScript.
