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

Unable to read properties off object spread in generic function parameter (regression with 4.6.1-rc) #47865

Closed
danvk opened this issue Feb 12, 2022 · 3 comments · Fixed by #47909
Assignees
Labels
Bug A bug in TypeScript Fix Available A PR has been opened for this issue

Comments

@danvk
Copy link
Contributor

danvk commented Feb 12, 2022

Bug Report

I noticed a regression between TypeScript 4.6.0-beta and 4.6.1-rc in my repo where parameters to generic functions that use object spread sometimes get an unusable type.

🔎 Search Terms

  • object spread
  • 4.6.1-rc

🕗 Version & Regression Information

  • This changed between versions 4.6.0-beta and 4.6.1-rc.

⏯ Playground Link

Playground link with relevant code

💻 Code

(see comment below for a simpler repro: #47865 (comment))

/** Extract params from an express path (e.g. '/students/:studentId'). */
export type ExtractRouteParams<T extends string> = string extends T
  ? Record<string, string>
  : T extends `${infer _Start}:${infer Param}/${infer Rest}`
  ? {[k in Param | keyof ExtractRouteParams<Rest>]: string}
  : T extends `${infer _Start}:${infer Param}`
  ? {[k in Param]: string}
  : {};

declare function get<T extends string>(
  path: T,
  handler: (params: ExtractRouteParams<T>) => void,
): void;

get('/path/:foo/to/:bar', ({foo, ...slugs}) => {
  const {bar} = slugs;  // ok in TS 4.6.0-beta but not in TS 4.6.1-rc
  console.log({bar});
});

get('/path/:foo/to/:bar', slugs => {
  const {bar} = slugs;  // ok in both
  console.log({bar});
});

🙁 Actual behavior

Using object spread in a function parameter produces an unusable type in TS 4.6.1-rc.

The type of slugs in the first example is:

 slugs: { bar: string; }

in TypeScript 4.6.0-beta but:

slugs: Omit<ExtractRouteParams<T>, "foo">

in TypeScript 4.6.1-rc.

These are both the same (assuming T = "/path/:foo/to/:bar") but the latter doesn't let me read any properties off of it.

🙂 Expected behavior

I should be able to read bar off of slugs.

@danvk
Copy link
Contributor Author

danvk commented Feb 14, 2022

Here's a simpler repro that doesn't involve template literal types. This (correctly) passes the type checker with TS 4.5.5 but raises a spurious error with 4.6.1-rc.

interface Slugs {
  foo: string;
  bar: string;
}

function call<T extends object>(obj: T, cb: (val: T) => void) {
  cb(obj);
}

declare let obj: Slugs;
call(obj, ({foo, ...rest}) => {
  console.log(rest.bar);
  //               ~~~ Property 'bar' does not exist on type 'Omit<T, "foo">'. ts(2339)
});

@RyanCavanaugh
Copy link
Member

Bisected to #47337

@RyanCavanaugh RyanCavanaugh added the Bug A bug in TypeScript label Feb 14, 2022
@RyanCavanaugh RyanCavanaugh added this to the TypeScript 4.6.1 milestone Feb 14, 2022
@RyanCavanaugh
Copy link
Member

@gabritto seems like we're failing to assign the inferred type to rest here; T shouldn't be appearing at all

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript Fix Available A PR has been opened for this issue
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants