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

Narrowing with in keyword and const identifier #61463

Closed
6 tasks done
yeomanse opened this issue Mar 21, 2025 Β· 5 comments
Closed
6 tasks done

Narrowing with in keyword and const identifier #61463

yeomanse opened this issue Mar 21, 2025 Β· 5 comments
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed

Comments

@yeomanse
Copy link

πŸ” Search Terms

"narrowing", "in", "const", "identifier"

βœ… Viability Checklist

⭐ Suggestion

Currently narrowing with the in keyword only works with a string literal in the if or if the identifier's type is a string literal. However, a const identifier with the type of a union of string literals feels like it should work the same way.
It can only ever be one of the literals and thus be a good candidate for narrowing.

For example:

export type Obj_Values = {
    values_string: string[];
    values_double: number[];
    values_int: number[];
    values_date: Date[];
    values_boolean: boolean[];
};
export type Obj_Key = keyof Obj_Values;
export type Obj_Choice<SELECTOR extends Obj_Key> = SELECTOR extends any ? Pick<Obj_Values, SELECTOR> : never;
export type Obj = Obj_Choice<Obj_Key>;

const obj: Obj;
const key: Obj_Key;

if (key in obj) {
    console.log(obj[key])  // Error below
    console.log((obj as Obj_Values)[key])  // Unfortunate work around
}

//Element implicitly has an 'any' type because expression of type 'keyof Obj_Values' can't be used to index type 'Obj'.
//  Property 'values_string' does not exist on type 'Obj'.(7053)

This might be easier said than done as there doesn't exist a way to convey what the post narrow type would even look like. But it seems like a good thing to talk about.

πŸ“ƒ Motivating Example

Further improving narrowing in situations where runtime code is dynamic enough to not want to use string literals but the literals are known in the types.

πŸ’» Use Cases

  1. What do you want to use this for?
    Code that uses types generated off of schemas but the runtime code doesn't need to be coupled to schema changes by requiring updates where narrowing happens.
  2. What shortcomings exist with current approaches?
    Lack of narrowing pushes you towards unsatisfactory workarounds.
  3. What workarounds are you using in the meantime?
    Casting to types that are lies or any.
@MartinJohns
Copy link
Contributor

Sounds like a duplicate of #55561 (comment).

@RyanCavanaugh
Copy link
Member

I don't see how this could really be made to work except through brute iteration, which would quickly become computationally infeasible in more complex situations. You have a keyof Obj_Values, and you have Obj_Choice<Obj_Key>, but there's nothing concrete tying the original keys to the output keys.

@RyanCavanaugh RyanCavanaugh added the Design Limitation Constraints of the existing architecture prevent this from being fixed label Mar 21, 2025
@yeomanse
Copy link
Author

Could you explain the brute iteration idea?

I don't know anything about the implementation but perhaps its not necessarily a narrowing because there is no real type to reduce down to. But perhaps its more of a new feature tacked on to the typeof obj within the if block that allows indexing with the identifier used for the in check.

@typescript-bot
Copy link
Collaborator

This issue has been marked as "Design Limitation" and has seen no recent activity. It has been automatically closed for house-keeping purposes.

@typescript-bot typescript-bot closed this as not planned Won't fix, can't repro, duplicate, stale Mar 24, 2025
@yeomanse
Copy link
Author

I understand a concern of computation if it basically does T[keyof T] by default. But a good compromise could be allowing the index into the obj and then have the dev bring their own resulting type.

...
export type Values = Obj_Values[keyof Obj_Values];

const obj: Obj;
const key: Obj_Key;

if (key in obj) {
    console.log(obj[key] as Values)  // Significantly more preferred
    console.log((obj as Obj_Values)[key])  // Than this
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed
Projects
None yet
Development

No branches or pull requests

4 participants