¿Cómo verificar el tipo de objeto en tiempo de ejecución en TypeScript?
runtime detect (4)
Editar : Quiero señalar a las personas que vienen de búsquedas que esta pregunta trata específicamente de tipos que no son de clase, es decir, formas de objetos según lo definido por la
interface
o el alias detype
. Para los tipos de clase, puede usar lainstanceof
de JavaScript de para determinar la clase de la que proviene una instancia, y TypeScript reducirá el tipo en el verificador de tipo automáticamente.
Los tipos se eliminan en tiempo de compilación y no existen en tiempo de ejecución, por lo que no puede verificar el tipo en tiempo de ejecución.
Lo que puede hacer es verificar que la forma de un objeto es lo que espera, y TypeScript puede afirmar el tipo en tiempo de compilación utilizando un
protector de tipo definido por el usuario
que devuelve verdadero (el tipo de retorno anotado es un "predicado de tipo" de la forma
arg is T
) si la forma coincide con sus expectativas:
interface A {
foo: string;
}
interface B {
bar: number;
}
function isA(obj: any): obj is A {
return obj.foo !== undefined
}
function isB(obj: any): obj is B {
return obj.bar !== undefined
}
function func(obj: any) {
if (isA(obj)) {
// In this block ''obj'' is narrowed to type ''A''
obj.foo;
}
else if (isB(obj)) {
// In this block ''obj'' is narrowed to type ''B''
obj.bar;
}
}
La profundidad que tome la implementación de la protección de tipo depende realmente de usted, solo necesita devolver verdadero o falso. Por ejemplo, como señala Carl en su respuesta, el ejemplo anterior solo verifica que las propiedades esperadas estén definidas (siguiendo el ejemplo en los documentos), no que se les haya asignado el tipo esperado. Esto puede ser complicado con tipos anulables y objetos anidados, depende de usted determinar qué tan detallado debe hacer la verificación de la forma.
Estoy tratando de encontrar una manera de pasar un objeto para que funcione y verificar que escriba en tiempo de ejecución. Este es un pseudocódigo:
func(obj:any){
if(typeof obj === "A"){
// do something
}
else if(typeof obj === "B"{
//do something else
}
}
a:A;
b:B;
func(a);
Pero typeof siempre devuelve "objeto" y no pude encontrar una manera de obtener el tipo real de "a" o "b". La instancia de tampoco funcionó y devolvió lo mismo. ¿Alguna idea de cómo hacerlo en un TypeScript?
¡¡¡Gracias por tu ayuda!!!
Ampliando la respuesta de Aaron, hice un transformador que genera las funciones de protección de tipo en tiempo de compilación. De esta manera no tiene que escribirlos manualmente.
Por ejemplo:
import { is } from ''typescript-is'';
interface A {
foo: string;
}
interface B {
bar: number;
}
if (is<A>(obj)) {
// obj is narrowed to type A
}
if (is<B>(obj)) {
// obj is narrowed to type B
}
Puede encontrar el proyecto aquí, con instrucciones para usarlo:
He estado jugando con la respuesta de Aaron y creo que sería mejor probar para typeof en lugar de simplemente indefinido, como este:
interface A {
foo: string;
}
interface B {
bar: number;
}
function isA(obj: any): obj is A {
return typeof obj.foo === ''string''
}
function isB(obj: any): obj is B {
return typeof obj.bar === ''number''
}
function func(obj: any) {
if (isA(obj)) {
console.log("A.foo:", obj.foo);
}
else if (isB(obj)) {
console.log("B.bar:", obj.bar);
}
else {console.log("neither A nor B")}
}
const a: A = { foo: 567 }; // notice i am giving it a number, not a string
const b: B = { bar: 123 };
func(a); // neither A nor B
func(b); // B.bar: 123
La pregunta de OP fue "Estoy tratando de encontrar una manera de pasar un objeto para que funcione y verificar que escriba en tiempo de ejecución".
Dado que una instancia de clase es solo un objeto, la respuesta correcta es usar una instancia de clase e instancia de cuando se necesita la verificación del tipo de tiempo de ejecución, use la interfaz cuando no.
En mi base de código, normalmente tendré una clase que implementa una interfaz y la interfaz se usa durante la compilación para la seguridad del tipo de tiempo de precompilación, mientras que las clases se usan para organizar mi código y hacer comprobaciones de tipo en tiempo de ejecución en script mecanografiado.
Funciona porque routerEvent es una instancia de la clase NavigationStart
if (routerEvent instanceof NavigationStart) {
this.loading = true;
}
if (routerEvent instanceof NavigationEnd ||
routerEvent instanceof NavigationCancel ||
routerEvent instanceof NavigationError) {
this.loading = false;
}
No trabajará
// Must use a class not an interface
export interface IRouterEvent { ... }
// Fails
expect(IRouterEvent instanceof NavigationCancel).toBe(true);
No trabajará
// Must use a class not a type
export type RouterEvent { ... }
// Fails
expect(IRouterEvent instanceof NavigationCancel).toBe(true);
Como puede ver en el código anterior, las clases se utilizan para comparar la instancia con los tipos NavigationStart | Cancel | Error.
El uso de instanceof en un Tipo o Interfaz no es posible ya que el compilador ts elimina estos atributos durante su proceso de compilación y antes de ser interpretados por JIT o AOT. Las clases son una excelente manera de crear un tipo que se puede utilizar antes de la compilación, así como durante el tiempo de ejecución de JS.