Skip to content

[nightly][regression] Mapped type over generic key unexpectedly makes key treated as optionalΒ #57860

Closed
@MichaelMitchell-at

Description

@MichaelMitchell-at

πŸ”Ž Search Terms

regression mapped type generic intersection undefined optional

πŸ•— Version & Regression Information

  • This changed in commit or PR #57549

⏯ Playground Link

https://www.staging-typescript.org/play?ts=5.5.0-dev.20240319#code/JYWwDg9gTgLgBAbwF4F84DMoRHA5EiAE1wG4BYAKEsoGMIA7AZ3kZoAsBTEAQzgF44SAHQQARgCsONGAAoElOIrhgsYAFyChzKMHoBzGQEoANJRSHyVK-IpK4MAJ5gOcACIcdANw6EAKk5cBYV10DwAeR2cIdDhWTh4APksFJUiXACFuQlduGF4BBDhgQg1tXT04NAAyNw9gbz8A5NsldABXemlgBjhRLIBRAA9ucAAbDjCAaTgOQZgOekJGOABrDgdouEzs3O4EmUJdjQAFYBoVsNPzsO2cvOM4aYAfPGLcBIfJhMMTs4vb3afBKIFJ2RRQDgwNpQehwQ55Sx2AD0SMUAD0MZi4P5nHgrhd8WE7txPnAXgAiYrkj6PBK4IrLegQeDcRiMYB6ejcUTjewQewBPF-ImA2m4ISguAosEynEuXDEgDaxwAumS4B1CBx0LofPTgIzmXBWezOdzeTB+Wk8ErVeLJdKZbLBbhNdrdcQGXAmSy2RyuTyXJaBbiFbtlSr7S1FI6nc7Q26dfQ9V6fca-WbA3yQ-KyvpxTIAEwAZkLhcMdklKDM1AoNjs1qGYFGZ2AMDl-BB0bBKgg6liMB0+kRSmrVgbgoA4hAiMTO4ViqVB+VKnAak2WzQ23LmnZ2p0YN1YXoZ4QhiNmxNprN5otlmsNjFp7Pdvt4dxftdCc+dvdHurcDeGkvh+OBv1PYkgS7OMIShGE4V2EdFDHFAgA

πŸ’» Code

import {z} from 'zod';


const schema = z.object({
    prop: z.string(),
});


{
    type DerivedType = z.infer<typeof schema>;

    type BadData = { id: string } & DerivedType;

    function badExample<K extends keyof BadData>(data: Pick<Pick<BadData, K | 'id'>, K>): Pick<BadData, K> {
        return data;
    //  ^^^^^^ Type 'Pick<Pick<Data, K | "id">, K>' is not assignable to type 'Pick<Data, K>'.
    //           Type 'Data[P] | undefined' is not assignable to type 'Data[P]'.
    //             Type 'undefined' is not assignable to type 'Data[P]'.
    //               Type 'undefined' is not assignable to type 'string'.(2322)    
    }
}

{
    type ExplicitType = {
        prop: string;
    }

    type GoodData = { id: string } & ExplicitType;

    function goodExample<K extends keyof GoodData>(data: Pick<Pick<GoodData, K | 'id'>, K>): Pick<GoodData, K> {
        return data;
    }
}

πŸ™ Actual behavior

Type error

πŸ™‚ Expected behavior

No type error. DerivedType expands to ExplicitType, yet produces different results.

Additional information about the issue

The title of this issue is probably inaccurate/misleading. I just wasn't sure how to describe the behavior. Note that I discovered this while using Airtable's internal schema lib, yet fortunately I was also able to reproduce this with Zod despite the implementations of the infer type being entirely different.

I bisected this to this commit 3b1b82a with @ahejlsberg's PR #57549

every-ts switch 309fd3db81955ef7a4dd55a80e333b2b767717a7
remote: Enumerating objects: 259180, done.
remote: Counting objects: 100% (379/379), done.
remote: Compressing objects: 100% (316/316), done.
remote: Total 259180 (delta 148), reused 230 (delta 41), pack-reused 258801
Receiving objects: 100% (259180/259180), 908.24 MiB | 7.64 MiB/s, done.
Resolving deltas: 100% (154585/154585), done.
remote: Enumerating objects: 868, done.
remote: Counting objects: 100% (847/847), done.
remote: Compressing objects: 100% (394/394), done.
remote: Total 868 (delta 486), reused 668 (delta 453), pack-reused 21
Receiving objects: 100% (868/868), 3.10 MiB | 10.90 MiB/s, done.
Resolving deltas: 100% (495/495), done.
Updating files: 100% (898/898), done.
Previous HEAD position was e5bf594753 Infer type predicates from function bodies using control flow analysis (#57465)
HEAD is now at 309fd3db81 Revert PR 56161 (#57853)
Building TypeScript...
TypeScript built successfully!
$ every-ts bisect bad 
You need to start by "git bisect start"

Do you want me to do it for you [Y/n]? y
status: waiting for both good and bad commits
status: waiting for good commit(s), bad commit known
$ every-ts bisect good v5.4.2
Bisecting: a merge base must be tested
remote: Enumerating objects: 2, done.
remote: Counting objects: 100% (2/2), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 2 (delta 0), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (2/2), 76.47 KiB | 2.64 MiB/s, done.
[d04e3489b0d8e6bc9a8a9396a633632a5a467328] Improve apparent type of mapped types (#57122)
Building TypeScript...
TypeScript built successfully!
$ every-ts bisect good       
Bisecting: 52 revisions left to test after this (roughly 6 steps)
remote: Enumerating objects: 207, done.
remote: Counting objects: 100% (185/185), done.
remote: Compressing objects: 100% (57/57), done.
remote: Total 207 (delta 141), reused 128 (delta 128), pack-reused 22
Receiving objects: 100% (207/207), 1.06 MiB | 6.50 MiB/s, done.
Resolving deltas: 100% (153/153), done.
[353ccb7688351ae33ccf6e0acb913aa30621eaf4] Ensure correct script kind and text when using cached sourceFile from scriptInfo (#57641)
Building TypeScript...
TypeScript built successfully!
$ every-ts bisect bad  
Bisecting: 26 revisions left to test after this (roughly 5 steps)
remote: Enumerating objects: 253, done.
remote: Counting objects: 100% (230/230), done.
remote: Compressing objects: 100% (75/75), done.
remote: Total 253 (delta 163), reused 155 (delta 155), pack-reused 23
Receiving objects: 100% (253/253), 604.74 KiB | 8.28 MiB/s, done.
Resolving deltas: 100% (175/175), done.
[a77370342dcdbd9273fe1716390d90a9b0d88fcc] Close "Design Limitation" automatically (#57554)
Building TypeScript...
announce
TypeScript built successfully!
$ announce
$ every-ts bisect good ; announce
Bisecting: 13 revisions left to test after this (roughly 4 steps)
remote: Enumerating objects: 7, done.
remote: Counting objects: 100% (7/7), done.
remote: Compressing objects: 100% (7/7), done.
remote: Total 7 (delta 0), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (7/7), 882.83 KiB | 5.38 MiB/s, done.
[877d9d316dd69ebd8253fe4c8915800415b0f455] Intl.NumberFormat: Add latest options, fix previous library discrepancies (#56902)
Building TypeScript...
TypeScript built successfully!
$ every-ts bisect good ; announce
Bisecting: 6 revisions left to test after this (roughly 3 steps)
remote: Enumerating objects: 1, done.
remote: Counting objects: 100% (1/1), done.
remote: Total 1 (delta 0), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (1/1), 550.26 KiB | 5.79 MiB/s, done.
[1d6d962d3132a901f5fb48be4a389c45faf5a74e] Update package-lock.json
Building TypeScript...
TypeScript built successfully!
$ every-ts bisect good ; announce
Bisecting: 3 revisions left to test after this (roughly 2 steps)
[320e17f1225e1a4f8fcf65554fe0ba2405d8a27f] "Annotate" exported object to fix named / namespace imports of our API in Node ESM (#57133)
Building TypeScript...
TypeScript built successfully!
$ every-ts bisect good ; announce
Bisecting: 1 revision left to test after this (roughly 1 step)
[3b1b82a6bf9267b4c99cbd8c4f25a3e578da0d44] Add optionality to mapped type indexed access substitutions (#57549)
Building TypeScript...
TypeScript built successfully!
$ every-ts bisect bad ; announce 
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[8336042ea3f84ad536a190ea75e47e74414e8081] Bump the github-actions group with 2 updates (#57624)
Building TypeScript...
TypeScript built successfully!
$ every-ts bisect good ; announce
3b1b82a6bf9267b4c99cbd8c4f25a3e578da0d44 is the first bad commit

Metadata

Metadata

Assignees

Labels

BugA bug in TypeScriptFix AvailableA PR has been opened for this issueRecent RegressionThis is a new regression just found in the last major/minor version of TypeScript.

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions