Búsqueda de sitios web

interfaz - ¿Soporta Typescript los "tipos subconjunto"?


Digamos que tengo una interfaz:

interface IUser {
  email: string;
  id: number;
  phone: string;
};

Luego tengo una función que espera un subconjunto (o coincidencia completa) de ese tipo. Tal vez pase un objeto completo, tal vez solo pase {email: "[email "}. Quiero que el verificador de tipos permita ambos.

Ejemplo :

function updateUser(user: IUser) {
  // Update a "subset" of user attributes:
  $http.put("/users/update", user);
}

¿TypeScript soporta este tipo de comportamiento ya? Podría encontrarlo muy útil, especialmente con paradigmas como Redux.

Para aclarar, el objetivo es:

  1. Evita reescribir una interfaz y establecer manualmente todos los atributos como opcionales.
  2. Evite la asignación de atributos inesperados (como errores ortográficos).
  3. Evita la lógica imperativa como las sentencias if, que renuncian a los beneficios de la verificación de tipos en tiempo de compilación.

ACTUALIZACIÓN: Typescript ha anunciado soporte para tipos mapeados, lo que debería resolver este problema una vez que se publique.

Respuesta #1

Vale la pena señalar que Partial<T>, como se sugiere en la respuesta aceptada, hace que todos los campos sean opcionales, lo cual no es necesariamente lo que necesitas.

Si quieres hacer que algunos campos sean obligatorios (por ejemplo, id y email), necesitas combinarlo con Pick:

type UserWithOptionalPhone = Pick<IUser, 'id' | 'email'> & Partial<IUser>

Alguna explicación:

Lo que hace Pick es que te permite especificar un subconjunto de la interfaz de manera sucinta (sin crear toda una nueva interfaz repitiendo los tipos de campos, como sugieren otras respuestas), y luego te permite usar esos, y solamente esos campos.

function hello1(user: Pick<IUser, 'id' | 'email'>) {
}

hello1({email: '@', id: 1}); //OK

hello1({email: '@'}); //Not OK, id missing

hello1({email: '@', id: 1, phone: '123'}); //Not OK, phone not allowed

Ahora, esto no es exactamente lo que necesitamos, ya que queremos permitir, pero no requerir el teléfono. Para hacer eso, "fusionamos" la versión parcial y la versión "seleccionada" de nuestro tipo creando un tipo de intersección, que luego tendrá id y email como campos obligatorios, y todo lo demás como opcional, exactamente como queríamos.

function hello2(user: Pick<IUser, 'id' | 'email'> & Partial<IUser>) {
}

hello2({email: '@', id: 1}); //OK

hello2({email: '@', id: 1, phone: '123'}); //OK

hello2({email: '@'}); //Not OK, id missing

Respuesta #2

Typescript ahora admite tipos parciales.

La forma correcta de crear un tipo parcial es:

type PartialUser = Partial<IUser>;

Respuesta #3

Lo que quieres es esto

type Subset<T extends U, U> = U;

esto asegura que U es un subconjunto de T y devuelve U como un nuevo tipo. por ejemplo:

interface Foo {
 name: string;
 age: number;
}

type Bar = Subset<Foo, {
 name: string;
}>;

no puedes agregar nuevas propiedades a Bar que no sean parte de Foo, y no puedes alterar los tipos de manera no compatible. esto también funciona de forma recursiva en objetos anidados.

Artículos relacionados