Skip to content

Partial Types (Optionalized Properties for Existing Types) #4889

@Gaelan

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

DanielRosenwasser

DanielRosenwasser commented on Sep 21, 2015

@DanielRosenwasser
Member

I brought this up recently with @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.

s-panferov

s-panferov commented on Sep 21, 2015

@s-panferov

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

Gaelan

Gaelan commented on Sep 21, 2015

@Gaelan
Author

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

kitsonk

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?

Gaelan

Gaelan commented on Sep 21, 2015

@Gaelan
Author

@kitsonk I was thinking of React.setState, or any other function that overwrites keys of one object with keys from another, not recursing.

jbondc

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}
fredgalvao

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});
dallonf

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} />
DomBlack

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' });
jbrantly

jbrantly commented on Oct 29, 2015

@jbrantly

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

Strate

Strate commented on Nov 11, 2015

@Strate

+1

xogeny

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.

102 remaining items

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

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

      Participants

      @hayeah@Ciantic@jbrantly@s-panferov@jbondc

      Issue actions

        Partial Types (Optionalized Properties for Existing Types) · Issue #4889 · microsoft/TypeScript