Typescript: Conditional union types with Record is split into multiple records

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

Hello, I'm not sure this is intended, but it destroys my solutions, as it doesn't follow a clear pattern. Not that I'm aware of in any case. I'm using a union type Record, followed by the tutorials of TypeScript's official documentation, but the minute I introduce conditional types into the mix, the record is simply split to multiple union Records, any thoughts on this would be appreciated.


TypeScript Version: 3.5.3


Search Terms: conditional types, union, null, record

Code

type RecordItems<Items extends string> =  Record<Items, boolean> 
type RecordItemsOrNull<Items extends string | null> = Items extends string ? Record<Items, boolean> : null

type RecordFruit = RecordItems<'orange' | 'apple' | 'banana'>
type RecordFruitOrNull = RecordItemsOrNull<'orange' | 'apple' | 'banana'>

const recordFruit: RecordFruit = {
  orange: true
}
const recordFruitOrNull: RecordFruitOrNull = {
  orange: true
}

Expected behavior:
Both the recordFruit and the recordFruitOrNull variables should break, since neither of them contain the entire record, the error should look like this:

test.ts:7:7 - error TS2739: Type '{ orange: true; }' is missing the following properties from type 'Record<"orange" | "apple" | "banana", boolean>': apple, banana

7 const recordFruit: RecordFruit = {
        ~~~~~~~~~~~

test.ts:10:7 - error TS2739: Type '{ orange: true; }' is missing the following properties from type 'Record<"orange" | "apple" | "banana", boolean>': apple, banana

10 const recordFruitOrNull: RecordFruitOrNull = {
         ~~~~~~~~~~~~~~~~~


Found 2 errors.

Actual behavior:
What actually happens is that the Record is split into union Records, e.g. Record<'orange', boolean> | Record<'apple', boolean> | Record<'banana', boolean> instead of following the regular pattern e.g. Record<'orange' | 'apple' | 'banana', boolean>

So there's only one error in my code sample, as the recordFruitOrNull variable passes:

test.ts:7:7 - error TS2739: Type '{ orange: true; }' is missing the following properties from type 'Record<"orange" | "apple" | "banana", boolean>': apple, banana

7 const recordFruit: RecordFruit = {
        ~~~~~~~~~~~


Found 1 error.

Playground Link:

Related Issues:

Most helpful comment

https://www.typescriptlang.org/docs/handbook/advanced-types.html#distributive-conditional-types

To prevent the distributive behavior, define the type like:

type NonDist<T> = [T] extends [Whatever] ? ...

All 2 comments

https://www.typescriptlang.org/docs/handbook/advanced-types.html#distributive-conditional-types

To prevent the distributive behavior, define the type like:

type NonDist<T> = [T] extends [Whatever] ? ...

That makes sense, I must've missed that in the documentation. Thanks for your elegant example, this issue can now be closed.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

wmaurer picture wmaurer  Β·  3Comments

Antony-Jones picture Antony-Jones  Β·  3Comments

fwanicka picture fwanicka  Β·  3Comments

jbondc picture jbondc  Β·  3Comments

DanielRosenwasser picture DanielRosenwasser  Β·  3Comments