Repository navigation
Partial Types (Optionalized Properties for Existing Types) #4889
Description
Activity
- addedSuggestionAn idea for TypeScriptAn idea for TypeScript
on Sep 21, 2015 DanielRosenwasser commented
on Sep 21, 2015 MemberMore actionsI brought this up recently with Mohamed Hegazy (@mhegazy), and it can really come in handy, especially
- When a constructor takes an options bag that has its own properties
interface Props { /*...*/ }; class Foo implements Props { constructor(opts: partial Props) { /*...*/} }
- When you want completion from members you're going to mix in but you don't want to implement all the required members.
let NewThing: NewMembers & (partial ThingImMixingInWith) = { /*completionHere*/
And I'm sure in other scenarios. How we choose to go about making this available is a separate matter.
Reacted by John Fawcett and tugendJust for bookkeeping, it's the same as $Shape from #2710.
Stanislav Panferov (@s-panferov) Does
$ShapematchdeepPartialorpartial? (deepPartialis recursive for sub-objects)When you want completion from members you're going to mix in but you don't want to implement all the required members.
And type guard against anything that isn't actually in the interface.
I would suspect
$Shapecould matchpartialat the least if notdeepPartial(e.g. a non-strict object literal check).Why wouldn't the behaviour always just be like
deepPartial... If you are already in the mode of not being strict, why would you ever suddenly not want that to be deep?Kitson Kelly (@kitsonk) I was thinking of
React.setState, or any other function that overwrites keys of one object with keys from another, not recursing.This would work nicely:
type foo = {a?:string, b?:string}; type fooStrict = foo & any!optional; // {a: string, b: string} type fooOptional = foo & any!strict; // {a?: string, b?: string}This would make typings for lodash/underscore pretty awesome.
Would also be usefull in cases where you get a partial model as a param on the constructor to build the actual model. This way we wouldn't need to create a sibling interface just to hold the same properties with different optionality.
class Person { name: string; surname: string; birthday: Date; numberOfThings: number; constructor(initialValues: partial Person) { this.name = initialValues.name; this.surname = initialValues.surname; this.birthday = initialValues.birthday; this.numberOfThings = initialValues.numberOfThings; } } new Person({name: 'Fred', surname: 'Galvão', numberOfThings: 2});
Reacted by Duan Yao, Ilja Kroonen, Bernardo Simoes, Vu Tran and Craig HarleyYes, this would be extremely handy! Another good use case is React "higher-order" components - I want to implement a component that acts just like another, "lower-order" component, except it automatically calculates the value of some of its props... example time:
interface IUserAvatarProps { url: string, size?: number } class UserAvatar extends React.Component<IUserAvatarProps, {}> { //... } interface ISmartUserAvatarProps implements partial IUserAvatarProps { userId: string } class SmartUserAvatar extends React.Component<ISmartUserAvatarProps, {avatarUrl: string}> { render() { return <UserAvatar url={this.state.avatarUrl} {...this.props} />; } componentWillMount() { // Fetch this.state.avatarUrl from this.props.userId } } // Later... <SmartUserAvatar id="1234" size={32} />
Yet another use case would be backbone models which have defaults and the constructor takes a partial set of attributes which override the defaults:
interface CarData { make: string, model: string } class Car extends Backbone.Model<CarData> { defaults: CarData = { make: 'BMW', model: '7' } } new Car({ model: 'X5' });
Dominic Black (@DomBlack) just pointed me to this. Thanks! Just for backward/forward reference, this was also discussed in #392.
+1
+1
For me, the big benefit of such functionality would be in being able to create typesafe APIs around the various libraries for immutable data structures like
updeeporimmutable.js. In both of these libraries, the efficiency of the library hinges on being able to modify data structures by passing in the "deltas" you wish to apply. And the fundamental issue with type safety is a way of expressing the types of these deltas in the context of a parametric type for the complete data.Of course, some basic language level functionality for implementing or expressing lenses would also be a potential way of addressing the issue.
- addedNeeds ProposalThis issue needs a plan that clarifies the finer details of how it could be implemented.This issue needs a plan that clarifies the finer details of how it could be implemented.
on Dec 9, 2015 There is a use case for the
Object.assign(from ES6).It could be defined using partial types like this:
function assign<T>(target: subset of T, ...sources: (subset of T)[]): T;Now I could do this:
var x = Object.assign({name:"Miguel Angelo"}, {age:32});As all arguments are stated to be subsets of T, the call expression type could be inferred to be:
interface __anonymous { name: string; age: number; }That would be assignable to the following type:
interface IPerson { name: string; age?: number; }It'd be assignable because
__anonymousis a super set ofIPerson.If this was possible, I could pass a variable
yof typeIPersonto the assign method. The following pattern is useful when changing an immutable object:var x = Object.assign({}, y, {name:"Miguel Angelo"});As
yis not known to have age or not, the resulting inferred type would have an optionalage, resulting in a type that is equal to the typeIPersonitself. The inference process could see that, and infer that this call expression type really isIPerson.The final case, would be when I state the type of T when calling. The inference algorithm could then make sure that the resulting type is what I expect, or give me an error:
var x = Object.assign<IPerson>({name:"Miguel"}, {age:32}); // the combined subsets is a valid IPerson var x = Object.assign<IPerson>({}, {name: "Miguel"}); // the combined subsets is a valid IPerson var x = Object.assign<ILawyer>(new Lawyer(), new Person()); // the combined subsets is a valid ILawyer var x = Object.assign<IPerson>({}, {age: 30}); // ERROR: the combined subsets is a valid PersonFor the keyword, it could be
partial,subset of, or anything, but I propose it to besubset of, because then there could be asuperset of. But thenpartialcould have acompletecounterpart... I don't know... anything is good enough.Does this make sense?
Reacted by Tyler Goodman, Seth Davenport and Alexander Ekdahl64 remaining items
- addedCommittedThe team has roadmapped this issueThe team has roadmapped this issue
on Nov 17, 2016 RyanCavanaugh commented
on Nov 17, 2016 MemberMore actionsIndeed
How do mapped types fill the need of the various
partialexamples shown above?Chris Barth (@cjbarth) See here for some examples. It's pretty much a giant sledgehammer solving several constraint problems at once.
// Example from initial report interface Foo { simpleMember: number; optionalMember?: string; objectMember: X; // Where X is a inline object type, interface, or other object-like type } // This: var foo: Partial<Foo>; // Is equivalent to: var foo: {simpleMember?: number, optionalMember?: string, objectMember?: X}; // Partial<T> in that PR is defined as this: // Make all properties in T optional interface Partial<T> { [P in keyof T]?: T[P]; }
Please note that
Partial<T>is now part of the default library file.Reacted by Jari Pennanen, Gabriel C. Troia, artem1, John Weisz, AJ, Glavin Wiechert, Stephen Wicklund, Aden Narkowicz, Bilal Aijazi, Ceyhun Can Ulker and 3 moreReacted by Timm Preetz, Jari Pennanen, Dallon Feldner, John Weisz, George Corney, Glavin Wiechert, Jarrod Mosen, Aden Narkowicz, Bilal Aijazi, Tom Spencer and 4 moreYou guys are all wizards to me. Hats off!
Mohamed Hegazy (@mhegazy) When you say it is in the "default library file", does that mean I should be able to use it with TS 2.1.x? Do I need to import something? It doesn't seem to work.
DanielRosenwasser commented
on Nov 28, 2016 MemberMore actionsMichael Tiller (@xogeny) yes, keep an eye out for it in TypeScript 2.1 (or in our nightly builds).
Daniel Rosenwasser (@DanielRosenwasser) I'm still confused. As far as I can tell, I'm running TypeScript 2.1 and I don't see it. Are you saying this will come out in a patch release of 2.1?
typescript@2.1.1isTS 2.1 RC. this does not have this change.
TS 2.1 will betypescript@2.1.3which has not shipped yet. you can usetypescript@nexttoday to get this feature working.Ah! OK, now I understand. I didn't realize these were release candidates. It would be much clearer if the version number reflected that, but I trust you had a good reason for doing it this way. I look forward to the final version!
From
npm view typescript:{ name: 'typescript', description: 'TypeScript is a language for application scale JavaScript development', 'dist-tags': { latest: '2.0.10', next: '2.2.0-dev.20161129', beta: '2.0.0', rc: '2.1.1', insiders: '2.0.6-insiders.20161017' } }
From a release tag perspective on npm, it is labelled a RC and will not install automatically.
Also the commit tag in GitHub also refects the nature of the release: https://github.com/Microsoft/TypeScript/releases/tag/v2.1-rc.
I believe the challenge is that some of the wider tooling requires installable versions to have a proper npm semver number (e.g. #.#.#).
Another good approach is to auto generate files without break the user code, then we can auto generate code using transformation templates.
- locked and limited conversation to collaborators
on Jun 19, 2018
Potential Use Cases
deepPartial)React.setState(partial)