Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inference lost on params of key-value function map #47147

Open
robertojmm opened this issue Dec 14, 2021 · 3 comments
Open

Inference lost on params of key-value function map #47147

robertojmm opened this issue Dec 14, 2021 · 3 comments
Labels
Bug A bug in TypeScript
Milestone

Comments

@robertojmm
Copy link

Bug Report

🔎 Search Terms

Inference Lost, function params map

🕗 Version & Regression Information

  • This is a crash
  • This changed between versions ______ and _______
  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about _________
  • I was unable to test this on prior versions because _______

⏯ Playground Link

Playground link with relevant code

💻 Code

export enum TRANSITIONS {
    A = 'a',
    B = 'b',
  }

export enum STATES {
    ONE = 'one',
    TWO = 'two',
}


export class StateOneObject {
    doSomething(){}
}
export class StateTwoObject {
    doSomething(){}
}

export type StatesToObjects = {
    [STATES.ONE]: StateOneObject;
    [STATES.TWO]: StateTwoObject;
};

export const ALLOWED_STATES_FROM_TRANSITION = {
    [TRANSITIONS.A]: [STATES.ONE, STATES.TWO],
    [TRANSITIONS.B]: [STATES.ONE],
  } as const;


function test<FromTransition extends TRANSITIONS>(
        transition: FromTransition,
        possibilites: {
            [FromState in typeof ALLOWED_STATES_FROM_TRANSITION[FromTransition][number]]: (
              state: StatesToObjects[FromState],
            ) => any;
          },
    ) {

    }

// Typing / Intellisense is not working 

// None of them work
test(TRANSITIONS.A, {
  // state is any but it should be StateOneObject
    one: state => { return state },
  // state is any but it should be StateTwoObject
    two: state => { return state }
})

// Only works the first one
test(TRANSITIONS.A, {
  // State is StateOneObject but state two is missing, normal error
    one: state => { return state.doSomething() },
})

// There is only one possibility but it doesn't work
test(TRANSITIONS.B, {
  // state is any but it should be StateOneObject
    one: state => { return state },
})

🙁 Actual behavior

Function state param shows error when adding the second state to the object. Please do hover over the state param.

🙂 Expected behavior

The state should be from the expected type

@RyanCavanaugh RyanCavanaugh added the Bug A bug in TypeScript label Dec 14, 2021
@RyanCavanaugh RyanCavanaugh added this to the Backlog milestone Dec 14, 2021
@ahejlsberg
Copy link
Member

The issue here is that generic mapped types as contextual types are not being instantiated during inference (we check for TypeFlags.Instantiable instead of using isGenericType(...)). Should be fixable, but there's always a chance that breaks something else.

@robertojmm
Copy link
Author

@ahejlsberg is there any walk around / alternative for doing this?

@ahejlsberg
Copy link
Member

ahejlsberg commented Dec 16, 2021

On closer inspection, I take back what I said about instantiation of contextual mapped types. That's actually not the issue.

It turns out your example works with the nightly build as long as you consistently use your string-based enum types in property names (your example uses regular property names in places where they should be string-based enum names). Specifically:

test(TRANSITIONS.A, {
  // state is StateOneObject
    [STATES.ONE]: state => { return state },
  // state is StateTwoObject
    [STATES.TWO]: state => { return state }
})

However, this unfortunately doesn't work in 4.5 or earlier releases. For those, the best work-around I can recommend is to eliminate the STATES enum and instead just use string literals:

export type StatesToObjects = {
    one: StateOneObject;
    two: StateTwoObject;
};

export const ALLOWED_STATES_FROM_TRANSITION = {
    [TRANSITIONS.A]: ['one', 'two'],
    [TRANSITIONS.B]: ['one'],
} as const;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript
Projects
None yet
Development

No branches or pull requests

3 participants