4 votes

Le générique TypeScript n'utilise que la valeur par défaut

J'essaie d'appliquer une valeur par défaut à un générique. Mais chaque fois que le générique n'est pas fourni, le générique prend par défaut la valeur donnée en argument. Je veux empêcher cela de se produire. Voici un exemple très simple :

type Data = Record<string, any>

const computeData = <D extends Data = {}>(input: D): D => {
  // Does some stuff to input
  return input;
}

const res = computeData<{ name: string }>({ name: 'john' })

Dans ce cas, le type de "res" est { name: string } comme prévu. Mais si le générique n'est pas fourni, j'ai besoin qu'il prenne par défaut le nom de {} qui est le type donné comme valeur par défaut pour le générique.

const computeData = <D extends Data = {}>(input: D): D => {
  // Does some stuff to input
  return input;
}

const res = computeData({ name: 'john' })

J'ai simplifié le code pour cet exemple. Je sais que dans le contexte de cet exemple, c'est un peu inutile.

EDIT : Le cas d'utilisation réel est un mocker de réseau qui permet aux utilisateurs d'ajouter des objets pour simuler des requêtes graphql. Je veux leur donner la possibilité de fournir des types de résolveurs. Cependant, s'ils ne fournissent pas les types, ils sont déduits des données initiales transmises - je veux éviter cela.

La classe définissant un enter image description here

Erreur de type car l'appel initial au constructeur infère le type pour le générique enter image description here

Cela fonctionne parce qu'un type explicite est donné lors de l'instanciation de la classe enter image description here

4voto

evelynhathaway Points 242

Vous pouvez contourner l'inférence de type en utilisant un autre paramètre de type et en filtrant les valeurs par défaut. Vous pouvez même le faire sans avoir à faire d'assertions de type.

Exemple de code

type Data = Record<string, any>

type NoInferData<DataType extends Data | void, InputType extends Data> = (
    DataType extends void
        // If it was void, there was no argument, so the type must be an empty object, or whatever you'd like
        ? Record<string, never>
        // Otherwise it should match the explcitly added data type
        : InputType
); 

const computeData = <
    // Type arugment for the type of Data you are providing
    DataType extends Data | void = void,
    // Type argument to store the inferred type of the input
    // - If its void, return the default type, in this example it's an empty object
    // - Otherwise, return the type of Data explicitly provided
    InputType extends Data = DataType extends void ? Record<string, never> : DataType
>(input: NoInferData<DataType, InputType>): NoInferData<DataType, InputType> => {
  // Does some stuff to input
  return input;
}

// Allows expliclty marking the data type
const explicitData = computeData<{ name: string }>({ name: 'john' })

// Doesn't allow data that doesn't match the explcit data type
const explicitDataError = computeData<{ pizza: string }>({ name: 'john' })

// Doesn't allow data that doesn't meet the default (no inferring)
const implictEmptyObjectError = computeData({ name: 'john' })

// Allows the default type of the empty object
const implictEmptyObject = computeData({})

Terrain de jeu TypeScript

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X