microsoft / microsoft/TypeScript

"Lookup" Type Broken When Using Generics

Ouverte
#62,770 3 commentaires 0 réactions 0 personnes assignées Voir sur GitHub

Personne n'a encore pris cette issue.

Domain: Mapped Types Help Wanted Possible Improvement
Langage dominant
Go
Étoiles
111k
Forks
14.3k
Merge moyen
2 j 4 h
PR mergées (30 j)
132

Description

🔎 Search Terms

"nested lookup with generic", "type lookup with generic"

🕗 Version & Regression Information
  • For doesNotWork1, this changed between versions 3.9.7 and 4.0.5.
  • For other "not working" examples, this is the behavior in every version I tried, and I reviewed the FAQ for entries about generics within lookup tables.
⏯ Playground Link

https://www.typescriptlang.org/play/?ts=5.9.3#code/JYOwLgpgTgZghgYwgAgOrDACwMIHsC2+uIyA3gFDJXIzAQA2AJgIwBcyIArvgEbQDclarQaMATOwDOYKKADmggL7lyoSLEQp0WAMoAHCAmBx6yCAA9IIRpLQYcBIiQrVkkg0ZPsuvAeWWq4NDwSMj6hsb0ADK4uADWnHpkQlTuEV52uh6RSipqwZrIeITEMfGJZpYQ1rbhntGxCUku1AiOxOzaDiUgueRgAJ4GyGVNADwAcnD4KBZWNshxEAO4MEXtIKOJAHzIALzJrgDaANLIoIvLq+s9W3pHUzMAuk8A-OzFTncP0xBPp08lIJyAB6EHICYAeQAKgBRdhYYC2JHIHhQeLVMwAN0xAHd7MgAAZLFZrT6lRqJQnIQAoBJcBkS0vVqZg4LYwLhUSg2oRqpBGMhcJwwDRcFBKtM9PQUFg4CLGJyQLgRbixXFkBy3BBZlB0VBbFAIGBOFAQOQYJwQAgwMBiG4CBBHhAxmc5tUFiTrnVIndtgAKACU7CxuGAApaVDaIGkyFVUDikjYI0pejGAHImZE07sDhHXDQ6Ewk8wADQpfMiJgSZBpnlONNl-PUTMZMQABgAzI3qIpga4ozG4wnq3d03XiNn9ocmwXREmAKzdpuV8TsDMOxEgOQN8uuMHINq6wxgegM2g1DWskWI2wrtyYIVMDjKrmX6AQXfN7IZDtiJfIXsVD3cELA8MA5VtEgUVlEVCUtRgIHPCBGGpSQH04J8+GQEx6FwXFkOQRhOBlTk9HRAwoEGDUhggWw0Lw+RLzlc4RXojCBT4ct9yhOEEUwFEACpcTZATYzVRiLixDsADoAE5pIAdnLAd5VwWiJmVVA1STUcThzacm33P0yNwCjBgDNxv3od4bi+FMASOAAiFt6Ecp5kAAH2QeDENAAi6UkTlmPwUBgHwbhLxRNjMJQQ1AvoHENU5ODrF8kBkNQ0BQkJdKcXFLyfKQlDkDi3AEtopKiUKvyUOkurP1SKz2Gq9LGH-VwVyTFrkPa4RC1XbzUqK-9AK4kDzDAiC7Wgq9ItsVV2NfOAeGlSrD0Na1T3OBDwGAGAz365ESHHEgYDFObLIiWgEC1EVrhJSQxohGF4QuhUKqVFU1XOEg4BABk8skSDsKxOBgHoZbVouKU4AGOR0Xg5TiBjd7JA0sAtPjEcUxdfS81cFz2GYNs2wAFhGoDqH3UDjymqD2Vmm8xMWrDIZIg8xQ2k8GTDPk9oO0Qjo5npYwJHCJXwKVaKenjXqZ1Hny++Mfuw-7kEB4G4FB8G2ZVmG4YR6wkejVT1M0tUO3YXS8Ya2ciyJxdbZXas0z+5VMGgNNy2UZQgA

💻 Code
interface WithCommon {
    field1: number;
    field2: string;
}

interface WithSpecial extends WithCommon {
    special: number;
}

interface SpecialLookup {
    special: WithSpecial;
}

interface CommonLookup extends SpecialLookup {
    common: WithCommon;
}

type Lookup<Name extends keyof CommonLookup> = {
    [K in keyof CommonLookup[Name]]?: CommonLookup[Name][K];
};

// NOTE: this is broken even with `keyof CommonLookup` — key `special` has to be removed to see current error return since with `CommonLookup` we do detect that `special` is not a shared key of all members
function someName<K extends keyof SpecialLookup>(): void {
    const works1: Lookup<'special'> = {
        field1: 1,
        field2: 'common',
        special: 203,
    };

    const works2: Lookup<'common'> = {
        field1: 5,
        field2: 'something',
        // correctly finds that this field should not be there
        special: 32,
    };

    // expectation is that `undefined` should be allowed due to property types showing that it should be
    // NOTE: this *was* working in v3.9.7
    const doesNotWork1: Lookup<K> = {
        // (property) special?: CommonLookup[K]["special"] | undefined — so at minimum this should be resolve to `undefined` since `never | undefined` resolves to `undefined`...
        special: undefined,
        field1: undefined,
        field2: undefined,
    };

    // expectation is that this would be able to correctly identify fields in common for this specific set of keys
    // NOTE: this does not work in any version available in playground
    const doesNotWork2: Lookup<K> = {
        special: 1004,
    }

    // expectation is that this would be able to correctly identify fields in common with all examples
    // NOTE: this does not work in any version available in playground
    const doesNotWork3: Lookup<K> = {
        field1: 15,
        field2: 'another'
    }
}
🙁 Actual behavior

When using a generic as the input to a "lookup" type, we should be able to correctly type the common fields of the "looked-up" interfaces.

What is interesting here is that we appear to get correct behavior from "code completion" (i.e. special is correctly typed as a property of Lookup<K> with type CommonLookup[K]["special"] | undefined), but actually setting the presented type value doesn't work.

🙂 Expected behavior

We should get correct typing for the generic Lookup<K> type:

  • doesNotWork1 should allow undefined as a value since field type evaluates to CommonLookup[K][Field] | undefined.
  • doesNotWork2 should allow number as value for field special since all interfaces within keyof SpecialLookup have special: number on the interface.
  • doesNotWork3 should allow values for field1 and field2 for the same reason as doesNotWork2.
Additional information about the issue

As I called out in my NOTE within the code example, this is also broken in a "simpler" example where the generic is K extends keyof CommonLookup. I presented the above code example because it is the most similar to my use case and I wasn't sure if fixing the "simpler" case would also fix the one where we are choosing a smaller key set.

Guide de contribution

Ouvrir le guide de contribution

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. Ouvrez une pull request qui référence le numéro de l'issue.

Piste de recherche

Commencez par le TypeScript Playground lié et reproduisez les exemples doesNotWork1–3 avec les versions du compilateur indiquées. Suivez le comportement de vérification de types des recherches génériques, puis vérifiez que les affectations attendues de undefined et des champs communs sont validées par le système de types sans autoriser les champs invalides.

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Évaluation

Stack technique
typescript
Domaine
compilers
Type d'issue
Bug
Difficulté
5/5
Temps estimé
Plus d'une semaine
Activité
À l'abandon
Clarté
Clairement spécifiée
Accessibilité débutants
35/100

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.