Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error output: Don't deep compare to native objects #26986

Open
cevek opened this issue Sep 9, 2018 · 3 comments
Open

Error output: Don't deep compare to native objects #26986

cevek opened this issue Sep 9, 2018 · 3 comments
Assignees
Labels
Committed The team has roadmapped this issue Domain: Error Messages The issue relates to error messaging Rescheduled This issue was previously scheduled to an earlier milestone Suggestion An idea for TypeScript

Comments

@cevek
Copy link

cevek commented Sep 9, 2018

TypeScript Version: 3.1.0-dev.201xxxxx

Search Terms:

Currently if we assign not assignable type to a variable, ts shows a deep 'not assignable' error with mark all not compatible fields. Even for a native objects like Array, Promise, Date etc...
But I think this is too noisily. All I want to know is that my type in not assignable to Array. Don't try to compare guts of Array with my type.

Code

type A = {a: string}
var y:A = {a: ''}
var x0: string = y; // Type 'A' is not assignable to type 'string'.  -- it's ok!
var x1: A[]  = y; // Property 'length' is missing in type 'A'.
var x2: Promise<A> = y; // Property 'then' is missing in type 'A'.
var x3: Date = y; // Property 'toDateString' is missing in type 'A'.
var x4: Node = y; // Property 'baseURI' is missing in type 'A'.

Expected behavior:

Actual behavior:

Playground Link:

Related Issues:

@DanielRosenwasser DanielRosenwasser added Suggestion An idea for TypeScript Needs Proposal This issue needs a plan that clarifies the finer details of how it could be implemented. In Discussion Not yet reached consensus Domain: Error Messages The issue relates to error messaging labels Sep 10, 2018
@DanielRosenwasser
Copy link
Member

I was actually thinking about this on my own today. I think we've tried to be general, and the issue is usually the abstractions over collections. So let's work to try to come up with some

Here's my take: you probably don't want to elaborate when comparing anonymous object types to be compared to concrete container types (Array, Promise, Map, Set - also, see #25752 for related work), wrapper types (String, Boolean, Number, Symbol), or some other common types (Date? Node?).

Let's dive into that last part? What's "common"? Is it anything with a value-side declaration that has a construct signature? For example, Date is both a type as well as a value in the global scope. Maybe getting the type of the value and probing to see if it has a constructor that returns the referenced type itself is a good signal here.

Let's also try to generalize: are we only thinking about anonymous types that come from object literals? Or are we also thinking about not elaborating between any two named types? I think this is harder. For example, you probably don't want to elaborate between a Date and an Array<T>, but what about an Iterable<T> and an Array<U>?

There's a lot here, but I'm really looking for more feedback to make the UX better. Maybe the first idea is good enough, but I'd like to hear your thoughts.

@cevek
Copy link
Author

cevek commented Sep 10, 2018

First I thought to skip elaborate all interfaces in the lib.d.ts but this is bad idea because there is a lot of interfaces like argument options AudioBufferOptions

I think we should skip elaborate only well known interfaces. We can mark them with some control comment like /*@no-elaborate*/

Your idea about if an interface has the same class is good. But some interfaces like Iterable, ReadonlyArray, ArrayLike, PromiseLike etc is not class interfaces and I don't want to elaborate them.
I don't think we can automatically split other types into elaborate and non elaborate, for example: Partial and Iterable.

Bad idea to elaborate Iterable and Array:

var x: Iterable<{x: number}> = {} as Array<{d: number}>

Type '{ d: number; }[]' is not assignable to type 'Iterable<{ x: number; }>'.
  Types of property '[Symbol.iterator]' are incompatible.
    Type '() => IterableIterator<{ d: number; }>' is not assignable to type '() => Iterator<{ x: number; }>'.
      Type 'IterableIterator<{ d: number; }>' is not assignable to type 'Iterator<{ x: number; }>'.
        Types of property 'next' are incompatible.
          Type '(value?: any) => IteratorResult<{ d: number; }>' is not assignable to type '(value?: any) => IteratorResult<{ x: number; }>'.
            Type 'IteratorResult<{ d: number; }>' is not assignable to type 'IteratorResult<{ x: number; }>'.
              Type '{ d: number; }' is not assignable to type '{ x: number; }'.
                Property 'x' is missing in type '{ d: number; }'.

Summary: if we can detect that interface is class interface or it is marked with control comment we don't elaborate.

@cevek
Copy link
Author

cevek commented Sep 10, 2018

Some examples:

var x: Promise<{ a: number; b: number }> = Promise.resolve({a: 1});
Type 'Promise<{ a: number; }>' is not assignable to type 'Promise<{ a: number; b: number; }>'.
  Types of property 'then' are incompatible.
    Type '<TResult1 = { a: number; }, TResult2 = never>(onfulfilled?: ((value: { a: number; }) => TResult1 | PromiseLike<TResult1>) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined) => Promise<...>' is not assignable to type '<TResult1 = { a: number; b: number; }, TResult2 = never>(onfulfilled?: ((value: { a: number; b: number; }) => TResult1 | PromiseLike<TResult1>) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined) => Promise<...>'.
      Types of parameters 'onfulfilled' and 'onfulfilled' are incompatible.
        Types of parameters 'value' and 'value' are incompatible.
          Type '{ a: number; }' is not assignable to type '{ a: number; b: number; }'.
            Property 'b' is missing in type '{ a: number; }'.

@RyanCavanaugh RyanCavanaugh added Committed The team has roadmapped this issue and removed In Discussion Not yet reached consensus Needs Proposal This issue needs a plan that clarifies the finer details of how it could be implemented. labels Oct 9, 2018
@RyanCavanaugh RyanCavanaugh added this to the TypeScript 3.3 milestone Oct 9, 2018
@RyanCavanaugh RyanCavanaugh removed this from the TypeScript 3.8.1 milestone May 1, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Committed The team has roadmapped this issue Domain: Error Messages The issue relates to error messaging Rescheduled This issue was previously scheduled to an earlier milestone Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

4 participants