question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Proposal: new "invalid" type to indicate custom invalid states

See original GitHub issue

Proposal

A new invalid type that is not assignable to or from any other types. This includes not being assignable to or from any or never. It probably shouldn’t even be assignable to invalid itself if that is possible, although I doubt that one really matters. I’d additionally suggest that, unlike other types, invalid | any is not reduced to any and invalid & never is not reduced to never.

The idea is to make sure that there is a compile error any time an invalid type is inferred or otherwise pops up in a users code.

invalid types would come from conditional types to represent cases where the conditional type author either expects the case to never happen, or expects that it might happen but intentionally wants that case to cause a compile error indicating to the user that something is invalid with the code they wrote.

The invalid type should also allow optionally passing an error message that would be displayed to the user when they encounter a compile error caused by the type that could give them a better idea of exactly what the problem is and how to fix it.

Motivating Examples

Allowing either true or false but not boolean - https://github.com/Microsoft/TypeScript/issues/23493#issuecomment-384369226

type XorBoolean<B extends boolean> = boolean extends B ? invalid<'only literal true or false allowed'> : boolean

declare function acceptsXorBoolean<B extends boolean & XorBoolean<B>>(arg: B): void

acceptsXorBoolean(true) // allowed
acceptsXorBoolean(false) // allowed

declare const unknownBoolean: boolean
acceptsXorBoolean(unknownBoolean)
// would have error message: 
// Argument of type 'boolean' is not assignable to parameter of type invalid<'only literal true or false allowed'>

It’s possible to write the above example today(playground link) using never instead of invalid, but it generates an error message saying: Argument of type 'boolean' is not assignable to parameter of type 'never'. which is very likely to be confusing to a user who encounters it.

Preventing duplicate keys - https://github.com/Microsoft/TypeScript/issues/23413#issuecomment-381369843

type ArrayKeys = keyof any[]
type Indices<T> = Exclude<keyof T, ArrayKeys>
type GetUnionKeys<U> = U extends Record<infer K, any> ? K : never
type CombineUnion<U> = { [K in GetUnionKeys<U>]: U extends Record<K, infer T> ? T : never }
type Combine<T> = CombineUnion<T[Indices<T>]>

declare function combine<
  T extends object[] &
    {
      [K in Indices<T>]: {
        [K2 in keyof T[K]]: K2 extends GetUnionKeys<T[Exclude<Indices<T>, K>]> ? invalid<"Duplicated key"> : any
      }
    } & { "0": any }
    >(objectsToCombine: T): Combine<T>


const result1 = combine([{ foo: 534 }, { bar: "test" }]) // allowed

const error1 = combine([{ foo: 534, dupKey: "dup1" }, { bar: "test", dupKey: "dup2" }]) // error

Today(playground link) using never instead of invalid the error message for error1 is:

Argument of type '[{ foo: number; dupKey: string; }, { bar: string; dupKey: string; }]' is not assignable to parameter of type 'object[] & { "0": { foo: any; dupKey: never; }; "1": { bar: any; dupKey: never; }; } & { "0": any...'.
  Type '[{ foo: number; dupKey: string; }, { bar: string; dupKey: string; }]' is not assignable to type '{ "0": { foo: any; dupKey: never; }; "1": { bar: any; dupKey: never; }; }'.
    Types of property '"0"' are incompatible.
      Type '{ foo: number; dupKey: string; }' is not assignable to type '{ foo: any; dupKey: never; }'.
        Types of property 'dupKey' are incompatible.
          Type 'string' is not assignable to type 'never'

which would be basically impossible to understand if you didn’t expect the function would reject duplicated keys. Using invalid<"Duplicated key"> however the error message could read:

Argument of type '[{ foo: number; dupKey: string; }, { bar: string; dupKey: string; }]' is not assignable to parameter of type 'object[] & { "0": { foo: any; dupKey: invalid<"Duplicated key">; }; "1": { bar: any; dupKey: invalid<"Duplicated key">; }; } & { "0": any...'.
  Type '[{ foo: number; dupKey: string; }, { bar: string; dupKey: string; }]' is not assignable to type '{ "0": { foo: any; dupKey: invalid<"Duplicated key">; }; "1": { bar: any; dupKey: invalid<"Duplicated key">; }; }'.
    Types of property '"0"' are incompatible.
      Type '{ foo: number; dupKey: string; }' is not assignable to type '{ foo: any; dupKey: invalid<"Duplicated key">; }'.
        Types of property 'dupKey' are incompatible.
          Type 'string' is not assignable to type 'invalid<"Duplicated key">'

Which gives a very clear hint that the problem is that dupKey is duplicated.

Conditional cases which should never happen

I could also see invalid potentially being used for some conditional types where there is a branch that presumably never gets taken because you are just using the conditional type for the infer capability. For example at the end of #21496 there is a type:

type AnyFunction = (...args: any[]) => any;
type ReturnType<T extends AnyFunction> = T extends (...args: any[]) => infer R ? R : never;

Maybe invalid<"should never happen"> is used instead of never for the false branch so it’s easier to track down the problem if it ever turns out the assumption that the branch will never be taken is wrong. (Of course if T is any, both the true and false branches are always taken so you might not want to change it away from never, but at least there’d be the option)

Related Issues

#20235 - Generics: Cannot limit template param to specific types - Could benefit from an approach like XorBoolean above. #22375 - how do i prevent non-nullable types in arguments - Solution here is basically the same idea as XorBoolean. The error message for this specific issue is already understandable but it shows there is more interest in the pattern. #13713 - [feature request] Custom type-error messages - Similar sounding idea, but it seems to be focused on changing the wording of existing error messages.

Search Terms

invalid type, custom error message, generic constraint, conditional types

Issue Analytics

  • State:open
  • Created 5 years ago
  • Reactions:206
  • Comments:27 (14 by maintainers)

github_iconTop GitHub Comments

29reactions
ds300commented, Mar 19, 2019

1000x yes to this.

As a library author it’s tempting to go wild with the expressivity of TS and end up producing some lovely useful safe abstractions… which give awful unhelpful type error messages when users make mistakes. So I will often trade off nice abstractions in favour of nice error messages.

This feature would mean I could avoid making those tradeoffs. I could give users wild, beautiful, feels-good-man type abstractions at the same time as giving users clear, domain-specific error messages.

23reactions
jcalzcommented, Jul 24, 2018

I just have to say that almost every time I use a conditional type I wish this proposal or something like it were implemented. If I could give this issue more than one 👍 I would.

Read more comments on GitHub >

github_iconTop Results From Across the Web

need more input or information regarding binary.write error ...
I'm trying to write protobuf *Timestamp.timestamp to binary, and the error I got is invalid type *Timestamp.timestamp and I've tried to no avail ......
Read more >
Invalid type for custom object - Salesforce Developer Community
Hi,. New to Apex but am getting Invalid Type on a custom object when try to for loop. The name of the object...
Read more >
ProposalLineItemService (v202211) | Ad Manager API
Audience segment rule is invalid. AUDIENCE_SEGMENT_RULE_TOO_LONG: Audience segment rule contains too many ad units and/or custom criteria.
Read more >
NetSuite Applications Suite - Invalid Purchaseunit Reference ...
Cloud · Cloud Applications · NetSuite. NetSuite Applications Suite. Table of Contents; Search. Contents. Expand AllCollapse All. What's New.
Read more >
Invalid Type Literal error when using AQL Expression - Sirius
Code: [Select all] [Show/ hide]. Invalid Type Literal model::myType ... They state that it could have something to do with model ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found