Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Super type constraints in TypeScript

Tags:

typescript

I'm trying to use the updeep library. A typical example of using updeep is something like this:

var person = {
  name: {
    first: 'Jane',
    last: 'West'
  }
};

var result = u({ name: { first: 'Susan' } }, person);

The idea here is that result will be a clone of person but with the value of name.first changed. You can imagine this function, u, defined in TypeScript as:

function u<T>(changes: {}, obj: T): T { ... }

This captures the fact that the type of the second argument is also the return type of the function. But what it does not express is that changes should be a super type of T.

What I'd like is some type checking on the first argument. The point is that the values present in the changes arguement should all be present in the T type parameter and match the types of their counterparts there. Expressing this allows us to check the changes argument to make sure it makes sense with respect to the type T (i.e., is a super type of T).

I'm not sure if this is possible in TypeScript. In languages like Java, you have the super keyword and it can be used to describe constraints on type parameters.

While TypeScript doesn't allow it, something like this expresses what I would like:

function u<T extends U,U extends {}>(changes: U, obj: T): T { ... }

Does anybody have an suggestions on how to express this? It would be great to have a typesafe system for performing such transformations.

Thanks.

like image 868
Michael Tiller Avatar asked Nov 13 '15 23:11

Michael Tiller


People also ask

What does ?: Mean in TypeScript?

Using ?: with undefined as type definition While there are no errors with this interface definition, it is inferred the property value could undefined without explicitly defining the property type as undefined . In case the middleName property doesn't get a value, by default, its value will be undefined .

What is constraints in TypeScript?

Type constraints are a very powerful way to supercharge your typescript codebases. For each generic type variable that you'd like to constrain, you would append the extends SomeTypeName to the definition. Example: const myFn = <T extends JsonValues, U extends FinancialData>() => {...}

How do I assign two TypeScript types?

TypeScript allows you to define multiple types. The terminology for this is union types and it allows you to define a variable as a string, or a number, or an array, or an object, etc. We can create union types by using the pipe symbol ( | ) between each type.

How do I use generic types in TypeScript?

By passing in the type with the <number> code, you are explicitly letting TypeScript know that you want the generic type parameter T of the identity function to be of type number . This will enforce the number type as the argument and the return value.


2 Answers

This has been possible since TypeScript 2.1 with Partial<T>

The original problem would be solved with

function u<T>(changes: Partial<T>, obj: T): T { ... }

like image 97
Crazometer Avatar answered Oct 17 '22 06:10

Crazometer


There's an open issue for something like this, called "Partial types" here: https://github.com/Microsoft/TypeScript/issues/4889

Facebook's Flow has a similar (but intentionally undocumented) $Shape<> type, which can be seen in the typings for React's setState().

So basically, no, there's no such feature that can do this automatically. However, you can kind of work around it by doing this manually:

interface IPartialPerson {
  name?: {
    first?: string;
    last?: string;
  };  
  someOptionalProperty?: string;
}

interface IPerson extends IPartialPerson {
  name: {
    first: string;
    last: string;
  };
}

Personally, I prefer to even avoid the latter part and just use optional properties as much as possible. Mandatory properties don't really protect you from anything; they're not non-nullable even though they look like it.

like image 1
DallonF Avatar answered Oct 17 '22 06:10

DallonF