microsoft / TypeScript

TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
https://www.typescriptlang.org
Apache License 2.0
101.19k stars 12.51k forks source link

Partial Types #11233

Closed RyanCavanaugh closed 8 years ago

RyanCavanaugh commented 8 years ago

This is a proposal for #4889 and a variety of other issues.

Use Cases

Many libraries, notably React, have a method which takes an object and updates corresponding fields on some other object on a per-key basis. It looks like this

function setState<T>(target: T, settings: ??T??) {
  // ...
}
let obj = { a: 2, b: "ok", c: [1] };
setState(obj, { a: 4 }); // Desired OK
setState(obj, { a: "nope"}); // Desired error
setState(obj, { b: "OK", c: [] }); // Desired OK
setState(obj, { a: 1, d: 100 }); // Desired error
setState(obj, window); // Desired error

Observations

A type partial T behaves as follows:

  1. Its properties are the same as the properties of T, except that those properties are now optional
  2. A S type is only assignable to type partial T if S has at least one property in common with T
    • Otherwise regular structural subtype/assignability rules apply
  3. The type partial (T | U) is equivalent to (partial T) | (partial U)
  4. The type partial (T & U) does not expand (this would interact poorly with rule 2)
  5. Any type T is always a subtype of partial T
  6. The type partial T is equivalent to T if T is any, never, null, or undefined

More thoughts to come later, likely

RyanCavanaugh commented 8 years ago

We'll close this with the assumption that #12114 (which is merged now) addresses all these use cases adequately - chime in with a new issue if that turns out not to be the case.

connor4312 commented 7 years ago

Hi @RyanCavanaugh, mapped types are fantastic, but it doesn't seem to address the questions above with deep partial types / deep subsets. Mapped types and Partial<> allow for a shallow subset, but all nested objects must be complete (though there are some dirty solutions). A PartialDeep generic would be fantastic.

The use case is that there is an API I'm building against which allows for object merges of very complex/deep objects.

Igorbek commented 7 years ago

@connor4312

type DeepPartial<T> = {
    [P in keyof T]?: DeepPartial<T[P]>;
};

this works just fine for me.

AlexGalays commented 7 years ago

@Igorbek

This gives zero control over Arrays for instance. It will iterate over their keys and allow you to access their method as nullable ones (concat, reduce, etc) which makes no sense. Same with a Date, etc.

Igorbek commented 7 years ago

ok, understood. My use case did require it. You then might be interested in #6606 with its mapping capabilities.

alvis commented 7 years ago

Also the related PR #17961. In fact, I'm facing the same problem. Will be extremely delighted when a solution becomes available.