Typescript infer with type mapping is unexpectedly dropping `undefined`

Viewed 30

In TypeScript, I have a function that takes a generic object whose values are all an instance of a generic class (MyClass<T> in the code sample below). I want the return value to be an object with the same keys but whose types are the corresponding T types.

Using TypeScript's infer keyword I've gotten this to work; however, if one of the T's is a union with undefined then the undefined seems to be dropped.

How can I get the undefined to carry through to the type of the corresponding key in the returned object?

Code sample:

class MyClass<T> {
  type = "MyClass";
}

type MyClassType =
  | MyClass<string>
  | MyClass<number>
  | MyClass<string | undefined>

interface MyClassInstanceByName {
  [name: string]: MyClassType
}

type ValueTypeByMyClassInstanceByName<T extends MyClassInstanceByName> = {
  [P in keyof T]: T[P] extends MyClass<infer U> ? U : never
}

function transformer<T extends MyClassInstanceByName>(input: T):
    ValueTypeByMyClassInstanceByName<T> {
  throw new Error("not implemented");
}

function test() {
  const result = transformer({
    shouldBeString: new MyClass<string>(),
    shouldBeNumber: new MyClass<number>(),
    shouldBeStringOrUndefined: new MyClass<string | undefined>(),
  });

  const isAString: string = result.shouldBeString;
  const isANumber: number = result.shouldBeNumber;
  const isStringOrUndefined: string = result.shouldBeStringOrUndefined;

  // The type of `result.shouldBeStringOrUndefined` above is inferred to be
  // `string`, but I want it to be inferred as `string | undefined`.
}

I've tried a whole bunch of different permutations of the type declarations, but each time I get something that works it "drops" the undefined or produces something incorrect, like never. I also tried to understand the difference between an optional property (i.e. a field name suffixed with ?) and a property whose type is | undefined. I tried to figure out some magic to mark the properties with undefined in their type union as optional with ? but haven't quite figured that out.

1 Answers

As @jcalz suggested, the fix was to enable strictNullChecks.

Related