Home > OS >  How to properly infer type in typescript
How to properly infer type in typescript

Time:12-01

I'm pretty new to typescript, this was probably answered before, but I can't find the answer anywhere, so here is the problem I'm trying to solve.

I have an enum

enum TableNames = {
  example: 'example'
  example2: 'example2',
}

and I have these types for my models

type TableName = keyof typeof TableNames

type TableState<L =any> = {
  list: Array<L>
}

type ExampleDataType = {
  id: string
  ...
}

type ExampleData2Type = {
  id: number
  ...
}

type TableStateType = {
  example: TableState<ExampleDataType>
  example2: TableState<ExampleData2Type>
}

type InterListType<TName extends TableName> = TableStateType[TName]['list'][number]

and I have this special function

const createPath = <TName extends TableNames, T = InferListType<TName>>(
  tableName: TName,
  record: T
) => {
  if (tableName === TableNames.example) {
    console.log(record) // how to get this to infer to "ExampleDataType"
  }

  if (tableName === TableNames.example2) {
    console.log(record) // how to get this to infer to "ExampleData2Type"
  }
}

The question is the comment above, right now I'm getting this

if (tableName === TableNames.example) {
  console.log(record) // T = InferListType<TName>
}

NOTE: typescript version is 3.7.4

CodePudding user response:

You are running into this issue: Typescript - How do I narrow type possibilities of a generic type in a switch statement?.

Here's why [it] won't work: Typescript has control-flow type narrowing for variables like tableName, but not for type parameters like T.

JCalz mentions a workaround in the comments that might help. Looks something like this:

type ExampleDataType = {
  id: string
  data1: string
}

type ExampleDataType2 = {
  id: string
  data2: string
}

type TableToRecordMap = {
  example: ExampleDataType
  example2: ExampleDataType2
}

type TableName = keyof TableToRecordMap

type TableState<L =any> = {
  list: Array<L>
}

type TableStateType = {
  [Table in TableName]: TableState<TableToRecordMap[Table]>
}

const createPath = <TName extends TableName>(
  tableName: TName,
  record: TableToRecordMap[TName]
) => {
  const partialTable: Partial<TableToRecordMap> = { [tableName]: record };

  if (partialTable.example) {
    console.log(partialTable.example)
  } else if (partialTable.example2) {
    console.log(partialTable.example2)
  }
};

Playground link

  • Related