Typescript: Overriding interface/types definitions to make properties required or optional

Created on 18 Jan 2020  路  7Comments  路  Source: microsoft/TypeScript

Similar to https://github.com/microsoft/TypeScript/issues/3402

I use this base type for all my DB models:

export declare type GraphCMSSystemFields = {
  id: string; // ID is required (I want it to be required by default, as it's the most common use case)
  createdAt?: string;
  status?: string;
  updatedAt?: string;
  __typename?: string;
}

But then I have a few exceptions:

import { GraphCMSSystemFields } from './GraphCMSSystemFields';

export declare type AssetTransformations = {
  id?: string; // Attempt to override the ID "required/optional" by using `?`
  height?: number;
  width?: number;
} & GraphCMSSystemFields;

I have no way of making the id optional if I make it required in the base type. Similarly, if I make it optional in the base type, I have no way to enforce it as being required on some sub types.

Isn't that a use-case that should be considered in the spec?
Also, there is no warning at all when doing this, it just silently doesn't work.

Most helpful comment

@Vadorequest Intersection types don't really make any guarantee about the type being inhabitable by an actual value in general. This is how we get things like branded types, or the ability to have an index signature of one type and properties that do not necessarily conform to that index signature

In the specific case of an intersection between a type with an optional property and a type with the same property required, I would not really expect an error. An intersection means that any value in this intersection will belong simultaneously to both constituent types. There is no intrinsic inconsistency between saying a value must satisfy both conditions at the same time. On the one hand the value may have an id and on the other it must have an id the result is that a value belonging to this type must have an id.

The solution I was alluding to is

export declare type AssetTransformations = {
  id?: string; // Attempt to override the ID "required/optional" by using `?`
  height?: number;
  width?: number;
} & Omit<GraphCMSSystemFields, 'id'> // just remove id, it will be re-added by the type above in effect overriding it

All 7 comments

This is a question not a bug report or a suggestion. Please use Stackoverflow for questions.

Your issue can be easily solved using Omit to take id out from GraphCMSSystemFields

It is a bug because it should at least throw a warning when doing that.

Also, I couldn't find such feature and though it was a missing feature, but you're right:
https://stackoverflow.com/questions/48215950/exclude-property-from-type

I'll let the issue open in regard to the missing warning.

Also, Omit omits a property, but doesn't seem to provide any way of overriding it. Couldn't find any example of property override. https://devblogs.microsoft.com/typescript/announcing-typescript-3-5-rc/#the-omit-helper-type

So, your "solution" isn't exactly what I'm looking for, and thus this issue seems to be a valid suggestion to the TS spec, IMHO.

@Vadorequest Intersection types don't really make any guarantee about the type being inhabitable by an actual value in general. This is how we get things like branded types, or the ability to have an index signature of one type and properties that do not necessarily conform to that index signature

In the specific case of an intersection between a type with an optional property and a type with the same property required, I would not really expect an error. An intersection means that any value in this intersection will belong simultaneously to both constituent types. There is no intrinsic inconsistency between saying a value must satisfy both conditions at the same time. On the one hand the value may have an id and on the other it must have an id the result is that a value belonging to this type must have an id.

The solution I was alluding to is

export declare type AssetTransformations = {
  id?: string; // Attempt to override the ID "required/optional" by using `?`
  height?: number;
  width?: number;
} & Omit<GraphCMSSystemFields, 'id'> // just remove id, it will be re-added by the type above in effect overriding it

@dragomirtitian Thanks for the extensive explanation and example. Much easier to understand that way.

On the one hand the value may have an id and on the other it must have an id the result is that a value belonging to this type must have an id.

I thought only the property of the base type was taken into account, wrongfully assumed because since it was marked as required in the base type, and then marked as optional in the extended type, it was still being required. (so I assumed the base type was given top priority, instead of thinking about it as an intersection rule like you explained)

I wouldn't expect an error either, just a warning, because the rule is pointless and is most likely the result of a misunderstood usage of TS. It makes sense, in such case, to warn the developers that they're not using the feature as expected.

@Vadorequest TS does not have warnings, it's an error or nothing at all 馃槀.

Also at this point this behavior is pretty set in stone as probably someone somewhere depends on it. I have often used an intersection in which the property appears optional and then appears again as required specifically to make a property required in a type that contained the property as optional initially.

Thanks, I'll rather close it then 馃槄

Was this page helpful?
0 / 5 - 0 ratings