Búsqueda de sitios web

Tipo de interfaz de TypeScript con claves opcionales de diferentes tipos y strictNullChecks


Estoy intentando crear la siguiente interfaz en TypeScript:

type MoveSpeed = "min" | "road" | "full";

interface Interval {
  min?: number,
  max?: number
}

interface CreepPlan {
  [partName: string] : Interval;
  move?:    MoveSpeed;
}

Sin embargo, esta sintaxis no es válida. El compilador dice La propiedad 'move' de tipo '"min" | "road" | "full" | undefined' no se puede asignar al tipo de índice de cadena 'Interval'..

Estoy usando la opción del compilador strictNullChecks:true, por lo que undefined se incluye implícitamente para la clave move?.

Sin embargo, esta sintaxis es válida:

type MoveSpeed = "min" | "road" | "full";

interface Interval {
  min?: number,
  max?: number
}

interface CreepPlan {
  [partName: string] : Interval;
  move:    MoveSpeed;            // 'move' is required
}

Quiero expresar la idea "CreepPlan consiste en pares string:Interval, excepto la clave opcional 'move' que es un par string:MoveSpeed." ¿Es posible expresar esto en TypeScript?

Respuesta #1

Declaraste que todas las propiedades en CreepPlan tienen valores de tipo Interval cuando escribiste:

interface CreepPlan {
  [partName: string] : Interval;
}

es decir, cualquier propiedad accesible mediante índice de cadena de CreepPlan será un Interval. Esto también se aplica a move, ya que foo['move'] es lo mismo que foo.move.

Sin embargo, al mirar Interval, no hay una estructura requerida para ello:

interface Interval {
  min?: number,
  max?: number
}

No hay campos obligatorios en ese contrato, pero sí requiere un objeto. Aceptará con gusto un objeto vacío, pero requiere algún objeto que podría tener propiedades min y max.

Tu tipo MoveSpeed, por otro lado, permite undefined:

La propiedad 'move' de tipo '"min" | "road" | "full" | undefined' no se puede asignar al tipo de índice de cadena 'Interval'.

Por lo tanto, Interval debe ser un objeto sin propiedades requeridas (lo cual string puede cumplir fácilmente) pero no permite undefined, lo cual MoveSpeed sí permite.

Tus tipos son disjuntos, pero no es obvio hasta que los resuelves una o dos veces.

Respuesta #2

Como se describe en la documentación de TypeScript, si usas propiedades indexables, entonces el tipo de retorno de todas las demás propiedades debería ser asignable al tipo de retorno del índice. Eso es porque foo['bar'] y foo.bar son lo mismo.

Entonces, ¿por qué funciona esto?

interface CreepPlan {
    [partName: string]: Interval;
    move: MoveSpeed;
}

Porque cualquier MoveSpeed es un valor válido de Interval (es un intervalo con ambos min/max indefinidos).

¿Por qué no funciona esto?

interface CreepPlan {
     [partName: string]: Interval;
     move?: MoveSpeed;
 }

move? tiene el tipo MoveSpeed más el tipo undefined. Si tiene el valor undefined, entonces no es asignable a Interval, por lo que el compilador muestra correctamente un error.

¿Cómo puedes arreglar esto? Simplemente haz que el indexador tenga undefined como un valor válido:

interface CreepPlan {
    [partName: string]: Interval | undefined;
    move?: MoveSpeed;
}

Artículos relacionados