Une autre question d'inférence. J'essaie d'empêcher Typescript de perdre des informations de type correctement déduites lorsque j'ai un type d'union enfoui dans un objet. Voici la situation :

Deux interfaces--Foo et Moo--et leur union FooMoo :

interface Foo { goo: string}
interface Moo { goo: number}
type FooMoo = Foo | Moo

const foo: Foo = {goo: "bar"}
const moo: Moo = {goo: 1}

Et un objet mixte qui contient (au moins) un de chaque :

const mixedObject = {
    foo,
    moo
}

Maintenant, évidemment, TS connaît les bons types de foo et moo, donc pas de problème ici :

let fooToo: Foo
let mooToo: Moo

fooToo = mixedObject.foo
mooToo = mixedObject.moo

Mais maintenant, disons que je veux dire à TS que mixedObject devrait être limité à uniquement foos et moos. Eh bien, cette restriction fait apparemment perdre à TS sa capacité à faire la distinction entre foo et moo, donc les erreurs de code suivantes :

type ManyFooMoos = {[K:string]: FooMoo}
const mixedObjectToo: ManyFooMoos = {
    foo,
    moo
}

// TS errors here because it can't narrow foo to Foo and moo to Moo;
// it just knows they are both FooMoos
fooToo = mixedObjectToo.foo
mooToo = mixedObjectToo.moo

Cette même chose peut se produire d'autres manières où la contrainte sur mixedObject qui fait perdre des informations à TS est plus indirecte. Par exemple:

interface ExportedObject {
    manyFooMoos: ManyFooMoos
} 

const exportedObject: ExportedObject = {
    manyFooMoos: mixedObject
}

fooToo = exportedObject.manyFooMoos.foo

Donc, ma question de base est : comment puis-je garder la contrainte sur mixedObject (c'est-à-dire que toutes ses propriétés doivent être Foos ou Moos) sans perdre les types spécifiques de ces Foos ou Moos dans mixedObject ?

Le code complet est disponible sur ce terrain de jeu.

1
sam256 7 sept. 2020 à 15:10

1 réponse

Meilleure réponse

Vous pouvez y parvenir en utilisant la fonction d'identité + les génériques. Cela permettra de garder la contrainte et d'éviter l'élargissement de type :

const createFooMoos = <T extends ManyFooMoos>(fooMoos: T) => fooMoos;

const mixedObjectToo = createFooMoos({
    foo,
    moo
}) // { foo: Foo, moo: Moo }

Terrain de jeux

3
Aleksey L. 7 sept. 2020 à 12:28