Búsqueda de sitios web

tipos - Problema con la tipificación en TypeScript


Por favor, eche un vistazo al siguiente fragmento de TypeScript. ¿Por qué eso NO genera un error de compilación? ¿No es obviamente un error de tipo? ¿Qué tengo que cambiar para que vuelva a ser seguro en cuanto al tipo? Muchas gracias

type A<P> = {
  p?: never,
  q?: Partial<P>
}

type B<P> = {
  p?: Partial<P> 
  q?: never
}

type C<P> = A<P> | B<P>

const c: C<{ a: number}> = {
  p: {
    a: 1,
    b: 2   // <------ Why is this allowed?!?
  }
}

console.log(c)

Haga clic aquí para una demostración

Respuesta #1

Este es un error conocido donde la verificación de propiedades en exceso no se aplica a tipos anidados que involucran uniones e intersecciones de la manera que la gente espera. La verificación de propiedades en exceso es una especie de complemento del sistema de tipos que solo se aplica a literales de objetos, por lo que cuando no se aplica, las cosas vuelven a la regla de subtipado estructural donde el tipo {a: A, b: B} es un subtipo de {a: A}, y por lo tanto un valor del tipo anterior debería ser asignable a una variable del tipo posterior. Puede que quieras dirigirte al problema en Github y darle un 👍 o explicar tu caso de uso si crees que es más convincente que los ya listados allí. Esperemos que algún día haya una solución.

Hasta entonces, sin embargo, no estoy seguro de lo que se puede hacer. El equivalente a nivel de tipo a las verificaciones de propiedades excesivas son los llamados tipos exactos, que no existen en TypeScript como tipos concretos. Hay formas de simulárlos usando funciones auxiliares genéricas e inferencia de tipos... en tu caso se vería algo así:

type Exactify<T, K extends keyof any>
  = T & { [P in Exclude<K, keyof T>]?: never };

type A<P, K extends keyof any=never> = {
  p?: never,
  q?: Partial<Exactify<P, K>>
}

type B<P, K extends keyof any=never> = {
  p?: Partial<Exactify<P, K>>
  q?: never
}

type C<P, K extends keyof any = never> = A<P, K> | B<P, K>

type KeyofKeyof<T> =
  keyof T | { [K in keyof T]: T[K] extends object ? keyof T[K] : never }[keyof T];

const asC = <T extends C<{ a: number }, KeyofKeyof<T>>>(c: T) => c;

const c = asC({
  p: {
    a: 1,
    b: 2   // <------ error
  }
})

Sí, es feo. No estoy seguro de si vale la pena para ti. La forma en que funciona es decir que C<P, K> es lo mismo que tu C<P> excepto que el tipo P se amplía para excluir explícitamente todas las propiedades adicionales en K. Luego usamos la función auxiliar asC() para inferir un tipo para K dado el parámetro pasado. Dado que las claves en cuestión están anidadas un nivel hacia abajo, necesité un tipo KeyofKeyof<T> para extraer claves de un nivel hacia abajo (no puedes hacerlo para todos los niveles sin generar tipos circulares).

El error deseado aparece ahora. ¿Yay? Supongo.

De todos modos, espero que eso ayude. ¡Buena suerte!

Artículos relacionados