Typescript: Infer type from a Sibling Prop

Created on 2 Aug 2019  ·  2Comments  ·  Source: microsoft/TypeScript

I did not find any information on Stack overflow or in existing issues so creating a new issue. Sorry if this is a duplicate.

I am not sure if this is a compiler limitation but this is what I am trying to achieve. I am creating a grid component that takes rows and columns. For example

interface Row {
   field1: string;
   field2: number;
}

interface Column<R> {
   field: keyof R;
   value: R[keyof R]; // This gives 'string | number '
}

Now if I create a columns array

const columns: Column<Row>[] = [
  { field: 'field1', value: 2 }, // There is not compilation error here which is expected because of 'R[keyof R]'
  { field: 'field2', value: 3 }
];

Is there a way I can modify value: R[keyof R] type so that in { field: 'field1', value: xyz } xyz can only be a string (Row['field1']).

Thanks.

Most helpful comment

To make it work you will need something like the following

interface Row {
   field1: string;
   field2: number;
}

interface Column<R, F extends keyof R = keyof R> {
   field: F; // for Row as R produce the set: 'field1' | 'field2'
   value: R[F]; // for Row as R produce the set: string | number
}

// for Row as R produce the set: Column<Row, 'field1'> | Column<Row, 'field2'>
type AnyRowField<R = Row, F extends keyof R = keyof R> = F extends keyof R ? Column<R, F> : never

And use it like this

// type checks
const columns: AnyRowField[] = [
  { field: 'field1', value: '2' }, 
  { field: 'field2', value: 3 }
]

// doesn't type check
const columns3: AnyRowField[] = [
  { field: 'field1', value: 2 }, // number not compatible with string
  { field: 'field2', value: '3' } // string not compatible with number
]

Example - https://bit.ly/2GG5nk2

All 2 comments

To make it work you will need something like the following

interface Row {
   field1: string;
   field2: number;
}

interface Column<R, F extends keyof R = keyof R> {
   field: F; // for Row as R produce the set: 'field1' | 'field2'
   value: R[F]; // for Row as R produce the set: string | number
}

// for Row as R produce the set: Column<Row, 'field1'> | Column<Row, 'field2'>
type AnyRowField<R = Row, F extends keyof R = keyof R> = F extends keyof R ? Column<R, F> : never

And use it like this

// type checks
const columns: AnyRowField[] = [
  { field: 'field1', value: '2' }, 
  { field: 'field2', value: 3 }
]

// doesn't type check
const columns3: AnyRowField[] = [
  { field: 'field1', value: 2 }, // number not compatible with string
  { field: 'field2', value: '3' } // string not compatible with number
]

Example - https://bit.ly/2GG5nk2

Thank you so much. It worked perfectly.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

fwanicka picture fwanicka  ·  3Comments

Antony-Jones picture Antony-Jones  ·  3Comments

uber5001 picture uber5001  ·  3Comments

bgrieder picture bgrieder  ·  3Comments

Roam-Cooper picture Roam-Cooper  ·  3Comments