Typescript: Typescript: Realistic type definition errors

Created on 20 Nov 2019  路  13Comments  路  Source: microsoft/TypeScript

Type checking, and type definitions errors could me made significantly easier to debug by boiling down the type definitions to something realistic.

  1. This first example shows the kind of errors I run into in actual development. It's totally unreadable.

Screen Shot 2019-11-20 at 3 58 27 PM

  1. This second example shows the same effect

Screen Shot 2019-11-20 at 4 01 34 PM
Screen Shot 2019-11-20 at 4 02 52 PM

For this second example, it would be a lot more useful if instead it just showed what the type realistically is, and what it was missing. Ie:

Property "badProp" does not exist on type `{
  requestId: string;
  type: string;
  id?: string;
  createdAt?: Date;
  data?: string;
}`

This could be a configuration thing, but I can't imagine any reason why a person would want cryptic error messages by default.

Unactionable

Most helpful comment

Just started to find a way to fold a type in error message. I'm new in typescript and sometimes I fight with it so hard to be safe typed. But when I see message like this I start to consider renaming this file back to .js.

image

All 13 comments

Kind of a difficult one.

It's not always true that the fully expanded type is more readable than the type alias.


I personally manually control the emit of my types (via type hackery) to try and get the best error message I can. (Rather than relying on TS' default emit algorithm)


Maybe something like this?

"badProp" does not exist on type `Partial<IEventTableNeeds> & IEventCreateNeeds`.
  "badProp" does not exist on type with keys requestId, type, id, createdAt, data

Maybe also do a Levenshtein distance on valid strings and suggest the closest match?

I personally manually control the emit of my types (via type hackery)

How do I get started on that?

And I'm sure this is one of those threads that once you start pulling it never stops... but, never-the-less, it couldn't be any worse than it is for some of these errors.

To force TS to expand types,
https://github.com/microsoft/TypeScript/issues/34556

To force TS to not expand types (situational, doesn't work for all cases),
https://github.com/microsoft/TypeScript/issues/34777#issuecomment-551993933

If you go down this rabbit hole, you may be cursed!


The Identity<> trick is a measure that forces the type to be expanded (almost?) always.

There are other tricks to force a type to be expanded if you can't rewrite a type to use the Identity<> trick.

Try looking at ts-toolbelt. I forget the name of the type.


One such trick is,

type Identity<T> = T;
type Merge<T> = Identity<{ [k in keyof T] : T[k] }>;

However, if T is a union type, this will break.

Hence, my feature request here, https://github.com/microsoft/TypeScript/issues/32909

A workaround is,

type DistributeMerge<T> = T extends any ? Merge<T> : never;

This merge trick can force the expansion of the top level object

This is the most helpful response I think I've ever gotten to a GH issue. Thank you very much.... I'm going down the rabbit hole.

A way to reproduce the error you saw would be very useful. We can sometimes improve error messages based on the use case.

Anyway it's not at all clear what the right rule is here; if one way were just always better than the other, that's the one we'd be doing by now (one would hope).

Let's say you had something like

interface Options {
  height: number;
  length: number;
  depth: number;
  width: number;
  color: string; 
  name: string;
  orientation: string;
  location: string; 
  age: number;
  // 30 other properties
}

and you use Options a lot throughout your code.

Somewhere, a function says

function fn(opts: Partial<Omit<Options, "orientation">>) {

}

and you call it incorrectly.

What would you want to see? Partial<Omit<Options, "orientation">> which says "Options, but it's all optional, and no orientation" or

{
  height?: number;
  length?: number;
  depth?: number;
  width?: number;
  color?: string; 
  name?: string;
  location?: string; 
  age?: number;
  // 30 other properties
}

where you will have to manually diff the property list to figure out which one got removed? That would be clearly worse in a lot of cases.

You can't just rely on "Well if the alias appears in the code, then use that" rule either because these types can appear in higher-order forms, e.g.

function fn<T, U extends keyof T>(obj: T, key: U, others: Partial<Omit<T, U>>) {

where you'd still want the unexpanded alias form.

I suppose the ideal would be some form of switching between the two. I don't expect that to have any easy way, but (if it was possible) it might be like:

  • Get error: Partial<Omit<Options, "orientation">>
  • click "show flattened"
  • have it turn to the long bare version
  • then click "show symbolic" to go back.

Anyway it's not at all clear what the right rule is here;

Perhaps there is a way to pass this logic off to user land? There are some things I'd like to try.

Speculatively, I think it might be effective to show flat versions of anything with a symbolic tree of over 3-4 nodes. Other times, it could perhaps fit both the symbolic and flat version one after another.

I've been getting really into advanced, conditional, and utility types for my projects.

I suppose the ideal would be some form of switching between the two.

A feature for this, mayhaps?

@weswigham That would definitely be a good way to go about it. Really, anything works for me, as long as I can see what the final list of properties and types for them is.

I'm going to post examples here as they come up.

Screen Shot 2020-01-03 at 11 18 19 AM

Screen Shot 2020-02-08 at 2 56 12 PM

Your last two examples just look like type emit problems.

The thing is, I feel like most people don't know how to get the current type system/emitter to produce "good" type emit. Especially for complex generic types.

There are tricks for these things. But sometimes, the types are just too large and some amount of difficult-to-read output is unavoidable.

So, errors can become unreadable, despite the team's best efforts, if users create types with unreadable emit in the first place.

I've found a solution for my scenario, and it is to create facade types that are simple and assert their equivalency to my more complex (and realistic) types elsewhere.

I'm not sure what the limitation of facade types is, but so far it's doing the trick.

Just started to find a way to fold a type in error message. I'm new in typescript and sometimes I fight with it so hard to be safe typed. But when I see message like this I start to consider renaming this file back to .js.

image

Was this page helpful?
0 / 5 - 0 ratings