Home > Software engineering >  Incorrect types. Why my new type is Never type?
Incorrect types. Why my new type is Never type?

Time:09-27

There is such interface

export interface DocExportRequest {
  id: string
  type: 'PDF' | 'CSV'
  [k: string]: any
}

Also I have jeneric type, which exclude [k: string] property

// see https://github.com/Microsoft/TypeScript/issues/12215#issuecomment-414782407
// disables `[k: string]: any;` indexing
export type KnownKeys<T> = {
  [K in keyof T]: string extends K ? never : number extends K ? never : K
} extends { [_ in keyof T]: infer U }
  ? U
  : never

Also I use ForkTsCheckerWebpackPlugin with config

const config = {
  async: isDevelopment,
  useTypescriptIncrementalApi: true,
  checkSyntacticErrors: true,
  reportFiles: ['**', '!**/__tests__/**', '!**/?(*.)(spec|test).*', '!**/src/setupProxy.*', '!**/src/setupTests.*'],
  silent: true,
};

and my tsconfig is

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@assets/*": ["src/assets/*"],
      "@src/*": ["src/*"]
   },
    "outDir": "./dist/",
    "sourceMap": true,
    "noImplicitAny": true,
    "target": "es5",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "typeRoots": [
      "./node_modules/@types"
    ],
    "types": [
      "node",
      "webpack-env",
    ],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "jsx": "react"
  },
  "include": ["src", "public"]
}

When I am applaying KnownKeys to DocExportRequest,

type TDocExportReq = KnownKeys<DocExportRequest>

I expect that it will be

type TDocExportReq {
  id: string
  type: 'PDF' | 'CSV'
}

But it return 'never'. Why?

Before that, the project was bootstrapped with create-react-app with similar configs, and all types were defined correct.

CodePudding user response:

That implementation of KnownKeys<T> was unfortunately broken in TypeScript 4.3; see microsoft/TypeScript#44142 for the relevant bug report. It's marked as "Needs Investigation" so the TS team isn't even sure if it qualifies as a bug or a new limitation, and nobody's even 100% sure what broke it.

Luckily there is a different implementation that works in TypeScript 4.2 and above, which uses key remapping to suppress index signatures:

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

And you can verify that this works:

type TDocExportReq = KnownKeys<DocExportRequest>
// type TDocExportReq = "id" | "type"

Playground link to code

  • Related