typescript - ¿Cuál es el tipo de registro en mecanografiado?
typescript2.0 (2)
¿Qué significa
Record<K, T>
en Typescript?
Typescript 2.1 introdujo el tipo de
Record
, describiéndolo en un ejemplo:
// For every properties K of type T, transform it to U function mapObject<K extends string, T, U>(obj: Record<K, T>, f: (x: T) => U): Record<K, U>
Y la página de
tipos avanzados
menciona el
Record
bajo el encabezado de tipos asignados junto a solo
Readonly
,
Partial
y
Pick
, en lo que parece ser su definición:
type Record<K extends string, T> = { [P in K]: T; }
Readonly, Parcial y Pick son homomorfos, mientras que Record no lo es. Una pista de que Record no es homomórfico es que no se necesita un tipo de entrada para copiar las propiedades de:
type ThreeStringProps = Record<''prop1'' | ''prop2'' | ''prop3'', string>
Y eso es.
Además de las citas anteriores, no hay otra mención de
Record
en
typescriptlang.org
.
Preguntas
-
¿Puede alguien dar una definición simple de lo que es
Record
? -
¿Es el
Record<K,T>
simplemente una forma de decir "todas las propiedades de este objeto tendrán el tipoT
"? Probablemente no todas las propiedades, ya queK
tiene algún propósito ... -
¿El genérico
K
prohíbe claves adicionales en el objeto que no sonK
, o las permite y solo indica que sus propiedades no se transforman enT
? -
Con el ejemplo dado:
type ThreeStringProps = Record<''prop1'' | ''prop2'' | ''prop3'', string>
¿Es exactamente lo mismo que esto ?:
type ThreeStringProps = {prop1: string, prop2: string, prop3: string}
- ¿Puede alguien dar una definición simple de lo que es
Record
?
Un
Record<K, T>
es un tipo de objeto cuyas claves de propiedad son
K
y cuyos valores de propiedad son
T
Es decir, la
keyof Record<K, T>
es equivalente a
K
, y el
Record<K, T>[K]
es (básicamente) equivalente a
T
- ¿Es el
Record<K,T>
simplemente una forma de decir "todas las propiedades de este objeto tendrán el tipoT
"? Probablemente no todos los objetos, ya queK
tiene algún propósito ...
Como usted nota,
K
tiene un propósito ... limitar las claves de propiedad a valores particulares.
Si desea aceptar todas las claves de valor de cadena posibles, podría hacer algo como
Record<string, T>
, pero la forma idiomática de hacerlo es usar una
firma de índice
como
{ [k: string]: T }
.
- ¿El genérico
K
prohíbe claves adicionales en el objeto que no sonK
, o las permite y solo indica que sus propiedades no se transforman enT
?
No exactamente "prohíbe" claves adicionales: después de todo, un valor generalmente puede tener propiedades que no se mencionan explícitamente en su tipo ... pero no reconocería que tales propiedades existen:
declare const x: Record<"a", string>;
x.b; // error, Property ''b'' does not exist on type ''Record<"a", string>''
y los trataría como propiedades excedentes que a veces se rechazan:
declare function acceptR(x: Record<"a", string>): void;
acceptR({a: "hey", b: "you"}); // error, Object literal may only specify known properties
y algunas veces aceptado:
const y = {a: "hey", b: "you"};
acceptR(y); // okay
Con el ejemplo dado:
type ThreeStringProps = Record<''prop1'' | ''prop2'' | ''prop3'', string>
¿Es exactamente lo mismo que esto ?:
type ThreeStringProps = {prop1: string, prop2: string, prop3: string}
¡Sí!
Espero que ayude. ¡Buena suerte!
Un registro le permite crear un nuevo tipo a partir de una unión. Los valores en la Unión se utilizan como atributos del nuevo tipo.
Por ejemplo, digamos que tengo una Unión como esta:
type CatNames = "miffy" | "boris" | "mordred";
Ahora quiero crear un objeto que contenga información sobre todos los gatos, puedo crear un nuevo tipo usando los valores en la Unión de CatName como claves.
type CatList = Record<CatNames, {age: number}>
Si quiero satisfacer este CatList, debo crear un objeto como este:
const cats:CatList = {
miffy: { age:99 },
boris: { age:16 },
mordred: { age:600 }
}
Obtienes seguridad de tipo muy fuerte:
- Si me olvido de un gato, me sale un error.
- Si agrego un gato que no está permitido, recibo un error.
- Si luego cambio los CatNames, recibo un error. Esto es especialmente útil porque es probable que CatNames se importe de otro archivo y se use en muchos lugares.
Ejemplo de reacción del mundo real.
Utilicé esto recientemente para crear un componente de estado. El componente recibiría una proposición de estado y luego representaría un icono. He simplificado bastante el código aquí para propósitos ilustrativos.
Tuve una unión como esta:
type Statuses = "failed" | "complete";
Utilicé esto para crear un objeto como este:
const icons: Record<
Statuses,
{ iconType: IconTypes; iconColor: IconColors }
> = {
failed: {
iconType: "warning",
iconColor: "red"
},
complete: {
iconType: "check",
iconColor: "green"
};
Entonces podría renderizar mediante la desestructuración de un elemento del objeto en props, así:
const Status = ({status}) => <Icon {...icons[status]} />
Si la unión de Statuses se amplía o cambia más tarde, sé que mi componente de estado no se compilará y obtendré un error que puedo solucionar de inmediato. Esto me permite agregar estados de error adicionales a la aplicación.
Tenga en cuenta que la aplicación real tenía docenas de estados de error a los que se hacía referencia en varios lugares, por lo que este tipo de seguridad fue extremadamente útil.