Verificación del tipo de interfaz con Typecript

Resuelto lhk asked hace 11 años • 30 respuestas

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 ?

lhk avatar Jan 20 '13 21:01 lhk
Aceptado

Puedes lograr lo que deseas sin la instanceofpalabra 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);
}
Fenton avatar Jan 20 '2013 15:01 Fenton

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.

vilicvane avatar Nov 16 '2015 10:11 vilicvane

¿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();
}
Caleb Macdonald Black avatar Jul 16 '2016 00:07 Caleb Macdonald Black

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;
    }
}
Joe Yang avatar Jul 13 '2016 09:07 Joe Yang

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();
}
Willem van der Veen avatar Oct 31 '2020 09:10 Willem van der Veen