Skip to content

Partial Types (Optionalized Properties for Existing Types) #4889

Description

@Gaelan
// Given:
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};

// And this:
var bar: deepPartial Foo;
// Is equivalent to:
var foo: {simpleMember?: number, optionalMember?: string, objectMember?: deepPartial X};

Potential Use Cases

  • Mongo Queries (deepPartial)
  • React.setState (partial)

Activity

  1. DanielRosenwasser commented on Sep 21, 2015

    @DanielRosenwasser
    Member

    I 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.

  2. s-panferov commented on Sep 21, 2015

    @s-panferov

    Just for bookkeeping, it's the same as $Shape from #2710.

  3. Gaelan commented on Sep 21, 2015

    @Gaelan
    Author

    Stanislav Panferov (@s-panferov) Does $Shape match deepPartial or partial? (deepPartial is recursive for sub-objects)

  4. kitsonk commented on Sep 21, 2015

    @kitsonk
    Contributor

    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 $Shape could match partial at the least if not deepPartial (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?

  5. Gaelan commented on Sep 21, 2015

    @Gaelan
    Author

    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.

  6. jbondc commented on Sep 22, 2015

    @jbondc
    Contributor

    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}
    
  7. fredgalvao commented on Sep 23, 2015

    @fredgalvao

    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});
  8. dallonf commented on Oct 12, 2015

    @dallonf

    Yes, 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} />
  9. DomBlack commented on Oct 29, 2015

    @DomBlack

    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' });
  10. jbrantly commented on Oct 29, 2015

    @jbrantly

    Dominic Black (@DomBlack) just pointed me to this. Thanks! Just for backward/forward reference, this was also discussed in #392.

  11. Strate commented on Nov 11, 2015

    @Strate

    +1

  12. xogeny commented on Nov 14, 2015

    @xogeny

    +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 updeep or immutable.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.

  13. added
    Needs ProposalThis issue needs a plan that clarifies the finer details of how it could be implemented.
    on Dec 9, 2015
  14. masbicudo commented on Jan 14, 2016

    @masbicudo

    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 __anonymous is a super set of IPerson.

    If this was possible, I could pass a variable y of type IPerson to the assign method. The following pattern is useful when changing an immutable object:

    var x = Object.assign({}, y, {name:"Miguel Angelo"});
    

    As y is not known to have age or not, the resulting inferred type would have an optional age, resulting in a type that is equal to the type IPerson itself. The inference process could see that, and infer that this call expression type really is IPerson.

    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 Person
    

    For the keyword, it could be partial, subset of, or anything, but I propose it to be subset of, because then there could be a superset of. But then partial could have a complete counterpart... I don't know... anything is good enough.

    Does this make sense?

  15. 64 remaining items

  16. RyanCavanaugh commented on Nov 17, 2016

    @RyanCavanaugh
    Member

    Indeed

  17. cjbarth commented on Nov 17, 2016

    @cjbarth

    How do mapped types fill the need of the various partial examples shown above?

  18. dead-claudia commented on Nov 17, 2016

    @dead-claudia

    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];
    }
  19. mhegazy commented on Nov 18, 2016

    @mhegazy
    Contributor

    Please note that Partial<T> is now part of the default library file.

  20. mpseidel commented on Nov 18, 2016

    @mpseidel

    You guys are all wizards to me. Hats off!

  21. xogeny commented on Nov 28, 2016

    @xogeny

    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.

  22. DanielRosenwasser commented on Nov 28, 2016

    @DanielRosenwasser
    Member

    Michael Tiller (@xogeny) yes, keep an eye out for it in TypeScript 2.1 (or in our nightly builds).

  23. xogeny commented on Nov 28, 2016

    @xogeny

    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?

  24. mhegazy commented on Nov 28, 2016

    @mhegazy
    Contributor

    typescript@2.1.1 is TS 2.1 RC. this does not have this change.
    TS 2.1 will be typescript@2.1.3 which has not shipped yet. you can use typescript@next today to get this feature working.

  25. xogeny commented on Nov 28, 2016

    @xogeny

    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!

  26. kitsonk commented on Nov 29, 2016

    @kitsonk
    Contributor

    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. #.#.#).

  27. danielmeza commented on Feb 8, 2017

    @danielmeza

    Another good approach is to auto generate files without break the user code, then we can auto generate code using transformation templates.

  28. locked and limited conversation to collaborators on Jun 19, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

CommittedThe team has roadmapped this issueSuggestionAn idea for TypeScript

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions