Skip to content

{[K in T]: (arg: K) => void}[T] has inconsistent/unsound behavior when T is a lazy-evaluated unionΒ #48734

@whzx5byb

Description

@whzx5byb

Bug Report

πŸ”Ž Search Terms

πŸ•— Version & Regression Information

  • This is the behavior in every version I tried, and some behavior changed between versions 4.5.5 and 4.6.2

⏯ Playground Link

Playground link with relevant code

πŸ’» Code

// Literal version
type U1 = {
    foo: (arg: "foo") => void;
    bar: (arg: "bar") => void;
}

// Mapped type version
type U2 = { [K in 'foo' | 'bar']: (arg: K) => void }
//   ^? type U2 = { foo: (arg: "foo") => void; bar: (arg: "bar") => void; }

type Is_U2_equivalent_to_U1 = U1 extends U2 ? U2 extends U1 ? true : false : false;
//   ^? type Is_U2_equivalent_to_U1 = true

function fn1<T extends 'foo' | 'bar'>(cb: {[K in T]: U1[K]}[T], arg: T): void {
    cb(arg); // correct error
}

function fn2<T extends 'foo' | 'bar'>(cb: {[K in T]: U2[K]}[T], arg: T): void {
    cb(arg); // // correct error before 4.6, compiles after 4.6
}

function fn3<T extends 'foo' | 'bar'>(cb: {[K in T]: (arg: K) => void}[T], arg: T): void {
    cb(arg); // should error but compiles
}

function fn4<T extends 'foo' | 'bar'>(cb: T extends unknown ? (arg: T) => void : never, arg: T): void {
    cb(arg); // correct error
}

πŸ™ Actual behavior

In 4.5.5, fn3 doesn't report an error.
In 4.6.2, fn2 and fn3 doesn't report an error.
I'm not sure but this maybe cause by #47109 or #47370 ?

πŸ™‚ Expected behavior

Error reported in fn1 and fn4 should also be reported in fn2 and fn3, because:

  • Provided that U1 is equivalent to U2, it is inconsistent where fn1 reports error but fn2 does not.
  • The pattern {[K in T]: G<K>}[T] has a distributive behavior over T, so I expect it behave the same as the conditional type form T extends unknown ? G<T> : never, which reports error correctly as fn4 shows.
  • An unsound call for fn3:
    fn3<'foo' | 'bar'>((x: 'foo') => assert(x === 'foo'), 'bar')

Metadata

Metadata

Assignees

No one assigned

    Labels

    DuplicateAn existing issue was already created

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions