3
votes

In Typescript, we have mapped types that can map properties from one type to another.

A common example is the Partial type, which makes all properties on the generic type 'optional'.

I would like to achieve the opposite, something like Required, where all properties are made 'required', regardless of their optionality up front.

Is such a thing possible?

(Since learned my question is superficial, please see update!)

Update:

Some context...

I'm learning about mapped types, and was inspired by the documentation here

type Proxy<T> = {
    get(): T;
    set(value: T): void;
}
type Proxify<T> = {
    [P in keyof T]: Proxy<T[P]>;
}
function proxify<T>(o: T): Proxify<T> {
   // ... wrap proxies ...
}
let proxyProps = proxify(props);

I thought to myself, hmm, "I wonder what mischief we can get up to with this?"

So I wrote something like:

type ProxyObservable<T> = Observable<T> & {
  [P in keyof T]: ProxyObservable<T[P]>;
};

Which is cool, because you can give it a type like this:

  interface Foo {
    myNum: number;
    myString: string;
    myObject: {
      someValue: number;
      anotherValue: Date;
    };
  }

And you get a ProxyObservable<Foo> type where every property is Observable of the same type, but also has matching navigation properties for child properties (which are also Observable).

const foo: ProxyObservable<Foo> = Proxify<Foo>(...)
foo.myNum.subscribe(...)
foo.myObject.subscribe(...)
foo.myObject.someValue.subscribe(...)

Unholy, I know. But cool.

The interesting problem comes in when you make a property (such as myObject) optional. Then Typescript will break on:

foo.myObject.someValue.subscribe(...)

Because it can't discern whether the type to union with Observable is 'undefined' or the object we defined. This is where my question came in.

BUT... I now also see that this isn't a problem with 'optional' properties, so much a problem with Discriminated Union types in general.

I realised that given the right context, Typescript may be able to infer the type of the foo.myObject property. I tried a few variations:

foo.myObject!.someValue //<- Nope
foo.myObject ? foo.myObject.someValue : ... //<- Nope
if(foo.myObject) foo.myObject.someValue //<- Nope

In my mind the Typescript compiler should be able to infer this!

Can anyone tell me why not?

3

3 Answers

4
votes

This is now possible and built into TypeScript as the Required type as of TypeScript 2.8.

http://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html

Here's the definition of it:

/**
 * Make all properties in T required
 */
type Required<T> = {
    [P in keyof T]-?: T[P];
};
3
votes

Type operators aren't powerful enough to do this as of yet, as @MeirionHughes says.

Why do you want this? One thing you can do is use inference from mapped types is to make a function which accepts a Partial<T> and returns a T. This actually works at the type level, but it's essentially impossible to actually implement such a function, since it would need to be able to take, say, and empty object at runtime, and magically know what properties to add to it. Can't be done. But anyway, here's the declaration:

declare function unpartial<T>(t: Partial<T>): T;

Say you have a concrete type with some optional and some required properties:

interface Stuff {
  foo?: string;
  bar?: number;
  baz: boolean;
  qux: () => void; 
}

If you had the unpartial() function and an instance of Stuff you could call it and get an object of the desired type:

declare const stuff: Stuff;
const unpartialStuff = unpartial(stuff);
type UnpartialStuff = typeof unpartialStuff;
// UnpartialStuff = { 
//  foo: string; 
//  bar: number; 
//  baz: boolean; 
//  qux: () => void;
// }

This is great but does not-great things at runtime, since unpartial is just declared but not implemented. You can have your cake and sort-of eat it too by doing this:

const unpartialStuff = (true as false) || unpartial(null! as Stuff);
type UnpartialStuff = typeof unpartialStuff;

That (true as false) evaluates at runtime to true but is interpreted by the type system as false, meaning that at runtime unpartial() never gets called but the compiler thinks it does and evaluates the types for you. And since we are lying to the compiler anyway, we might as well not waste a variable name on the fake instance of Stuff; hence (null! as Stuff).

Let's put that all in one place:

declare function unpartial<T>(t: Partial<T>): T;
interface Stuff {
  foo?: string;
  bar?: number;
  baz: boolean;
  qux: () => void; 
}
const unpartialStuff = (true as false) || unpartial(null! as Stuff);
type UnpartialStuff = typeof unpartialStuff;

There you go, you've lied and cheated to fool the compiler to calculate UnpartialStuff from Stuff. It's up to you if this trickery is worth it in your case. Good luck!


Update

Can anyone tell me why not?

Well, the reason why it doesn't work is that keyof (A | B) = (keyof A) & (keyof B), so keyof (A | undefined) = (keyof A) & never = never. So as soon as you try to map over the keys of a possibly-undefined type you find that there are no keys.

This identity, where the keys of a union are the intersection of the keys of each constituent, is the "safe" identity, since the only keys you know exist on a union are the keys that exist in every constituent. That is working as intended.

For mapping over the keys of unions, you'd need some unsafe operation, or conditional mapped types or other type operator so you could map over each constituent. And unfortunately those type operators don't currently exist in TypeScript. 🙁

I'm not sure how best to proceed. Recursive mapped types without more powerful type operations are really limited, and you've hit one of those limits. Maybe someone has better suggestions, other than "wait for future TypeScript features". Good luck again!

2
votes

Unfortunately, I don't think you can do it generically with a type map. (Happy to be proven wrong and I'll delete this answer if so). There is an issue for this very question on github too, but some of the proposed solutions don't work for me.

To do it generically, I think you'd need type subtraction, which is still being discussed. You'd use the subtraction to 'remove' undefined from the type union T | undefined.

The only way I know of "forcing" certain properties to be there, on the back of something partial, is to use a union with explicitly known properties:

interface Foo {
  a: number;
}

let a: Partial<Foo> & { a: number } = {}

Type '{}' is not assignable to type '{ a: number; }'. Property 'a' is missing in type '{}'.

An example:

class Person {
  id: string;
  name: string = "John";
  address: string = "empty";

  constructor(init: Partial<Person> & { id: string }) {
    Object.assign(this, init);
  }
}   

let person = new Person({ id: "1234" });

where you force the id to be there, but everything else is partial.