Explícame qué significa keyof typeof en TypeScript
Ejemplo:
enum ColorsEnum { white = '#ffffff', black = '#000000', } type Colors = keyof typeof ColorsEnum;La última fila es equivalente a:
type Colors = "white" | "black"pero como funciona?
Esperaría que typeof ColorsEnum devuelva algo como "Object" y luego keyof "Object" para no hacer nada interesante. Pero obviamente estoy equivocado.
Para encontrar el tipo de cualquier valor, usamos la operación typeof. Por ejemplo
const user = { getPersonalInfo(){}, getLocation(){} }Aquí el usuario es un valor, por lo que aquí el operador typeof es útil
type userType = typeof userAquí userType proporciona información de tipo de que el usuario es un objeto que tiene dos propiedades getPersonalInfo y getLocation y ambas son funciones que devuelven vacío
Ahora, si desea encontrar las claves de usuario, puede usar keyof
type userKeys = keyof userTypeque dice userKeys= 'getPersonalInfo'| 'obtenerUbicación'
Tenga cuidado si intenta obtener la clave del usuario como
type userKeys = keyof userobtendrá un error 'usuario' se refiere a un valor, pero se usa como un tipo aquí. ¿Querías decir 'typeof user'?
Para comprender el uso de keyof typeof en TypeScript, primero debe comprender qué son los tipos literales y la unión de tipos literales . Entonces, primero explicaré estos conceptos y luego explicaré keyof y typeof individualmente en detalle. Después de eso, volveré a enum para responder lo que se pregunta en la pregunta. Es una respuesta larga, pero los ejemplos son fáciles de entender.
Los tipos literales en TypeScript son tipos más específicos de string , number o boolean . Por ejemplo, "Hello World" es una string , pero una string no es "Hello World" . "Hello World" es un tipo de string de tipo más específico, por lo que es un tipo literal.
Un tipo literal se puede declarar de la siguiente manera:
type Greeting = "Hello" Esto significa que el objeto de tipo Greeting solo puede tener un valor de string "Hello" y ningún otro valor de string o cualquier otro valor de cualquier otro tipo, como se muestra en el siguiente código:
let greeting: Greeting greeting = "Hello" // OK greeting = "Hi" // Error: Type '"Hi"' is not assignable to type '"Hello"'Los tipos literales no son útiles por sí solos, sin embargo, cuando se combinan con tipos de unión, alias de tipo y protectores de tipo, se vuelven poderosos.
El siguiente es un ejemplo de unión de tipos literales :
type Greeting = "Hello" | "Hi" | "Welcome" Ahora el objeto de tipo Greeting puede tener el valor "Hello" , "Hi" o "Welcome" .
let greeting: Greeting greeting = "Hello" // OK greeting = "Hi" // OK greeting = "Welcome" // OK greeting = "GoodEvening" // Error: Type '"GoodEvening"' is not assignable to type 'Greeting'keyof solo keyof de algún tipo T te da un nuevo tipo que es una unión de tipos literales y estos tipos literales son los nombres de las propiedades de T . El tipo resultante es un subtipo de cadena.
Por ejemplo, considere la siguiente interface :
interface Person { name: string age: number location: string } Usar el operador keyof en el tipo Person le dará un nuevo tipo como se muestra en el siguiente código:
type SomeNewType = keyof Person Este SomeNewType es una unión de tipos literales ( "name" | "age" | "location" ) que se realiza a partir de las propiedades de tipo Person .
Ahora puede crear objetos de tipo SomeNewType :
let newTypeObject: SomeNewType newTypeObject = "name" // OK newTypeObject = "age" // OK newTypeObject = "location" // OK newTypeObject = "anyOtherValue" // Error...keyof typeof juntos en un objeto Como ya sabrá, el operador typeof le da el tipo de un objeto. En el ejemplo anterior de la interfaz Person , ya conocíamos el tipo, por lo que solo teníamos que usar el operador keyof en el tipo Person .
Pero, ¿qué hacer cuando no sabemos el tipo de un objeto o simplemente tenemos un valor y no un tipo de ese valor como el siguiente?
const bmw = { name: "BMW", power: "1000hp" } Aquí es donde usamos keyof typeof juntos.
El tipo de typeof bmw te da el tipo: { name: string, power: string }
Y luego el operador keyof le da la unión de tipo literal como se muestra en el siguiente código:
type CarLiteralType = keyof typeof bmw let carPropertyLiteral: CarLiteralType carPropertyLiteral = "name" // OK carPropertyLiteral = "power" // OK carPropertyLiteral = "anyOther" // Error...keyof typeof en una enumEn TypeScript, las enumeraciones se usan como tipos en tiempo de compilación para lograr la seguridad de tipos para las constantes, pero se tratan como objetos en tiempo de ejecución . Esto se debe a que se convierten en objetos simples una vez que el código TypeScript se compila en JavaScript. Entonces, la explicación de los objetos anteriores también es aplicable aquí. El ejemplo dado por OP en la pregunta es:
enum ColorsEnum { white = '#ffffff', black = '#000000', } Aquí ColorsEnum existe como un objeto en tiempo de ejecución, no como un tipo. Entonces, necesitamos invocar a los operadores keyof typeof juntos como se muestra en el siguiente código:
type Colors = keyof typeof ColorsEnum let colorLiteral: Colors colorLiteral = "white" // OK colorLiteral = "black" // OK colorLiteral = "red" // Error...¡Eso es todo! Espero que ayude.
Concepto erróneo común sobre TypeScript
TypeScript a menudo se describe como una capa de tipo sobre el tiempo de ejecución de JavaScript. Como si tipos y valores vivieran en planos separados. Sin embargo, en TypeScript, algunas cosas son tipos y valores al mismo tiempo.
Esto es cierto para:
¿Cuándo puedes usar keyof ?
La palabra clave keyof solo funciona en el nivel de tipo. No puede aplicarlo a un valor de JavaScript.
¿Cuándo necesitas keyof typeof ?
Cuando se trata de algo que es un tipo y un valor al mismo tiempo (como una clase o una enumeración), pero está interesado específicamente en cuál es el tipo de ese valor.
El ejemplo más simple:
const foo = { bar: 42 }; // foo is a value type Foo = typeof foo; // Foo is the type of foo type KeyOfFoo = keyof Foo; // "keyof Foo" is the same as "keyof typeof foo", which is "bar"En general, cuando ves esto:
type A = keyof typeof B; la parte typeof B le dice a TypeScript que mire el tipo de B. Puede pensar en ello como convertir B en su tipo. Algo así como lanzar un objeto bidimensional a un espacio unidimensional.
Dado que typeof B es un tipo, no un valor, ahora podemos usar keyof en él.
Ejemplo
Las clases son tipos y valores. Puede llamarlos, pero también puede usar keyof en ellos.
declare class Foo { static staticProperty: string; dynamicProperty: string; } type Constructor = typeof Foo; type Instance = Foo; type A = keyof Constructor; // "prototype" | "staticProperty" type B = keyof Instance; // "dynamicProperty" Al usar typeof junto con keyof , podemos alternar entre usar keyof contra el tipo de instancia y el tipo de constructor .
keyof toma un tipo de objeto y devuelve un tipo que acepta cualquiera de las claves del objeto.
type Point = { x: number; y: number }; type P = keyof Point; // type '"x" || "y"' const coordinate: P = 'z' // Type '"z"' is not assignable to type '"x" | "y"'. typeof se comporta de manera diferente cuando se invoca en objetos javascript, a cuando se invoca en tipos typescript.
"undefined", "object", "boolean", "number", "bigint", "string", "symbol", "function" type Language = 'EN' | 'ES'; const userLanguage: Language = 'EN'; const preferences = { language: userLanguage, theme: 'light' }; console.log(typeof preferences); // "object" type Preferences = typeof preferences; // type '{language: 'EN''; theme: string; }' Debido a que el segundo tipo de typeof preferences está en una expresión de tipo, en realidad es el propio typeof de TypeScript el que se llama, y no el de javascript.
Debido a que keyof es un concepto de TypeScript, llamaremos a la versión de typeof de TypeScript.
keyof typeof el tipo de un objeto javascript y devolverá un tipo que es la unión de sus claves. Debido a que puede inferir el valor exacto de las claves, puede devolver una unión de sus tipos literales en lugar de simplemente devolver "cadena".
type PreferenceKeys = keyof typeof preferences; // type '"language" | "theme"'Una enum crea un object instanciado. Con typeof obtenemos el tipo generado automáticamente de esta enum .
Ahora podemos obtener todos los índices con keyof para asegurarnos de que Colors solo pueda contener uno de ellos.