question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Utility type `KnownKeys<T>` broken on TypeScript 4.3

See original GitHub issue

Description

The following code no longer compiles after upgrading from TypeScript 4.2.4 to 4.3.2

(Click here to expand)
import { App } from "@slack/bolt";
const app = new App();
app.command("/say_hi", async ({ ack, respond }) => {
    await ack();
    await respond({
        blocks: [{ type: "section", text: { text: "hi", type: "plain_text" } }],
    //  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    // error TS2345: Argument of type '{ blocks: { type: string; text: { text: string; type: string; }; }[]; response_type: "ephemeral"; }' is not assignable to parameter of type 'string | RespondArguments'.
    //  Object literal may only specify known properties, and 'blocks' does not exist in type 'RespondArguments'.    });
        response_type: "ephemeral",
    });
});

I have narrowed down the problem to the utility type KnownKeys<T> in /src/types/helpers.ts.

type type KnownKeys<T> = {
    [K in keyof T]: string extends K ? never : number extends K ? never : K;
} extends { [_ in keyof T]: infer U }
    ? U
    : never;

type Foo = {
    foo: "Foo";
    bar: "Bar";
    [key: string]: any;
};

type Result = KnownKeys<Foo>;
// type of `Result` is `never` in TypeScript 4.3.2
// type of `Result` is `"foo" | "bar"` in TypeScript 4.2.4

KnownKeys<T> failing to resolve the keys has caused RespondArguments in /src/types/utilities.ts to omit the property “blocks” by calling Pick with never as the 2nd type argument:

type RespondArguments = Pick<
  ChatPostMessageArguments,
  Exclude<KnownKeys<ChatPostMessageArguments>, 'channel' | 'text'> // <-- KnownKeys<T> fails here
> & { /* ... */ };

…which in turn causes RespondFn to report an error when “blocks” is included in respond({ /* ... */ })

What type of issue is this? (place an x in one of the [ ])

  • bug
  • enhancement (feature request)
  • question
  • documentation related
  • example code related
  • testing related
  • discussion

Requirements (place an x in each of the [ ])

  • I’ve read and understood the Contributing guidelines and have done my best effort to follow them.
  • I’ve read and agree to the Code of Conduct.
  • I’ve searched for any related issues and avoided creating a duplicate issue.

Bug Report

Filling out the following details about bugs will help us solve your issue sooner.

Reproducible in:

package version: 3.3.0

node version: 15.12.0

OS version(s): OSX 11.3.1

Steps to reproduce:

(See description)

Also, /types-tests/utilities.test-d.ts should show an error after upgrading to TypeScript 4.3 (See attachment)

Expected result:

KnownKeys<T> should work properly in TypeScript 4.3.
Maybe add a test case to cover type Result = KnownKeys<Foo>; should not infer type never.

Actual result:

KnownKeys<T> infers never in TypeScript 4.3.

Attachments:

image

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Reactions:13
  • Comments:9 (8 by maintainers)

github_iconTop GitHub Comments

10reactions
lokshunhungcommented, Jun 1, 2021

I’ve looked into the implementation of KnownKeys<T> a bit, the current implementation (after formatting by hand):

type KnownKeys<T> = {
  [K in keyof T]: string extends K ? never :
                  number extends K ? never :
                  K;
} extends { [_ in keyof T]: infer U }
  ? U
  : never;

The first clause reads as “Given a type T, for each key as K in T, if string extends K or number extends K then return never, else return K”, the second clause reads as "Given the result object type of the first clause (which is an object type { [Key in keyof T]: Key | never }), return a union type of the values of this result object type.

I think this implementation can be further simplified to the following for better readability:

type KnownKeys<T> = {
  [K in keyof T]: string extends K ? never :
                  number extends K ? never :
                  K;
}[keyof T]

…which essentially does the same thing, doing the string extends and number extends check and returning K, then using keyof T to index the intermediate object (but now without needing infer!)

1reaction
schoeningcommented, Nov 5, 2021

@lokshunhung thats the only implementation that worked for me, thank you 😃 The other KnownKeys examples returned a never type on my machine unfortunately.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Utility type KnownKeys<T> broken on TypeScript 4.3 #951
The first clause reads as "Given a type T , for each key as K in T , if string extends K or...
Read more >
Documentation - TypeScript 4.3
When considering how two properties with the same name relate to each other, TypeScript will only use the “reading” type (e.g. the type...
Read more >
Announcing TypeScript 4.3 Beta - Microsoft Developer Blogs
Let's dive in to what TypeScript 4.3 brings! Separate Write Types on Properties. In JavaScript, it's pretty common for APIs to convert values ......
Read more >
The New Features and Breaking Changes in TypeScript 4.4
Using the so-called type guard concept, the TypeScript compiler ... does not exist on type 'string | number' because TypeScript 4.3 does not ......
Read more >
Why is the Exclude utility type not working as expected in ...
issueType >]: boolean; }; }; };. typescript · types · enums · conditional-types · mapped-types.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found