Verificación del tipo de interfaz con Typecript
Esta pregunta es análoga directa a la verificación de tipo de clase en TypeScript
Necesito saber en tiempo de ejecución si una variable de tipo any implementa una interfaz. Aquí está mi código:
interface A {
member: string;
}
var a: any = { member: "foobar" };
if (a instanceof A) alert(a.member);
Si ingresa este código en el patio de juegos mecanografiado, la última línea se marcará como un error, "El nombre A no existe en el ámbito actual". Pero eso no es cierto, el nombre existe en el ámbito actual. Incluso puedo cambiar la declaración de la variable var a:A={member:"foobar"};
sin quejas del editor. Después de navegar por la web y encontrar la otra pregunta sobre SO, cambié la interfaz a una clase, pero no puedo usar objetos literales para crear instancias.
Me preguntaba cómo el tipo A podría desaparecer así, pero una mirada al javascript generado explica el problema:
var a = {
member: "foobar"
};
if (a instanceof A) {
alert(a.member);
}
No hay representación de A como interfaz, por lo tanto, no es posible realizar comprobaciones del tipo de tiempo de ejecución.
Entiendo que javascript como lenguaje dinámico no tiene concepto de interfaces. ¿Hay alguna forma de verificar las interfaces?
El autocompletado del área de juegos de mecanografiado revela que mecanografiado incluso ofrece un método implements
. Como puedo usar lo ?
Puedes lograr lo que deseas sin la instanceof
palabra clave, ya que ahora puedes escribir guardias de tipos personalizados:
interface A {
member: string;
}
function instanceOfA(object: any): object is A {
return 'member' in object;
}
var a: any = {member: "foobar"};
if (instanceOfA(a)) {
alert(a.member);
}
Muchos miembros
Si necesita verificar muchos miembros para determinar si un objeto coincide con su tipo, puede agregar un discriminador. El siguiente es el ejemplo más básico y requiere que usted administre sus propios discriminadores... necesitaría profundizar en los patrones para asegurarse de evitar discriminadores duplicados.
interface A {
discriminator: 'I-AM-A';
member: string;
}
function instanceOfA(object: any): object is A {
return object.discriminator === 'I-AM-A';
}
var a: any = {discriminator: 'I-AM-A', member: "foobar"};
if (instanceOfA(a)) {
alert(a.member);
}
En TypeScript 1.6, la protección de tipos definida por el usuario hará el trabajo.
interface Foo {
fooProperty: string;
}
interface Bar {
barProperty: string;
}
function isFoo(object: any): object is Foo {
return 'fooProperty' in object;
}
let object: Foo | Bar;
if (isFoo(object)) {
// `object` has type `Foo`.
object.fooProperty;
} else {
// `object` has type `Bar`.
object.barProperty;
}
Y tal como mencionó Joe Yang: desde TypeScript 2.0, incluso puedes aprovechar el tipo de unión etiquetado.
interface Foo {
type: 'foo';
fooProperty: string;
}
interface Bar {
type: 'bar';
barProperty: number;
}
let object: Foo | Bar;
// You will see errors if `strictNullChecks` is enabled.
if (object.type === 'foo') {
// object has type `Foo`.
object.fooProperty;
} else {
// object has type `Bar`.
object.barProperty;
}
Y también funciona con switch
.
¿Qué tal los protectores de tipo definidos por el usuario? https://www.typescriptlang.org/docs/handbook/advanced-types.html
interface Bird {
fly();
layEggs();
}
interface Fish {
swim();
layEggs();
}
function isFish(pet: Fish | Bird): pet is Fish { //magic happens here
return (<Fish>pet).swim !== undefined;
}
// Both calls to 'swim' and 'fly' are now okay.
if (isFish(pet)) {
pet.swim();
}
else {
pet.fly();
}
Typescript 2.0 introduce la unión etiquetada
Características de TypeScript 2.0
interface Square {
kind: "square";
size: number;
}
interface Rectangle {
kind: "rectangle";
width: number;
height: number;
}
interface Circle {
kind: "circle";
radius: number;
}
type Shape = Square | Rectangle | Circle;
function area(s: Shape) {
// In the following switch statement, the type of s is narrowed in each case clause
// according to the value of the discriminant property, thus allowing the other properties
// of that variant to be accessed without a type assertion.
switch (s.kind) {
case "square": return s.size * s.size;
case "rectangle": return s.width * s.height;
case "circle": return Math.PI * s.radius * s.radius;
}
}
Escriba guardias en Typecript:
TS dispone de protecciones tipográficas para este fin. Lo definen de la siguiente manera:
Alguna expresión que realiza una verificación en tiempo de ejecución que garantiza el tipo en algún ámbito.
Básicamente, esto significa que el compilador TS puede limitar el tipo a un tipo más específico cuando tiene suficiente información. Por ejemplo:
function foo (arg: number | string) {
if (typeof arg === 'number') {
// fine, type number has toFixed method
arg.toFixed()
} else {
// Property 'toFixed' does not exist on type 'string'. Did you mean 'fixed'?
arg.toFixed()
// TSC can infer that the type is string because
// the possibility of type number is eliminated at the if statement
}
}
Volviendo a su pregunta, también podemos aplicar este concepto de protección de tipos a objetos para determinar su tipo. Para definir una protección de tipos para objetos, necesitamos definir una función cuyo tipo de retorno sea un predicado de tipo . Por ejemplo:
interface Dog {
bark: () => void;
}
// The function isDog is a user defined type guard
// the return type: 'pet is Dog' is a type predicate,
// it determines whether the object is a Dog
function isDog(pet: object): pet is Dog {
return (pet as Dog).bark !== undefined;
}
const dog: any = {bark: () => {console.log('woof')}};
if (isDog(dog)) {
// TS now knows that objects within this if statement are always type Dog
// This is because the type guard isDog narrowed down the type to Dog
dog.bark();
}