Skip to content

Incorrect any type distilled from extended generic after typeof check #41797

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

Open
dennis-c opened this issue Dec 3, 2020 · 4 comments
Open
Labels
Bug A bug in TypeScript
Milestone

Comments

@dennis-c
Copy link

dennis-c commented Dec 3, 2020

TypeScript Version: 4.1.2 and 4.2.0-dev.20201202

Search Terms: extended generic typeof any

Code

type Identifier = string;

interface Identifiable {
	readonly id: Identifier;
}

class DropdownItem<ID extends Identifier> implements Identifiable {
	public readonly id: ID;
	public readonly name: string;

	public constructor(id: ID, name: string) {
		this.id = id;
		this.name = name;
	}
}

const defaultItemGetCells = (item: Identifiable): string[] => {
	if (item instanceof DropdownItem) {
		return [ item.id, item.name ]; //item.id is incorrectly typed as any
	} else {
		return [ item.id ]; //item.id is correctly typed as string
	}
};

Expected behavior:
The type of item.id on line 19 is correctly identified as a string (just like on line 21). It should always be at least a string, even is no generic is passed to DropdownItem (And I can't pass a generic because it's plain old javascript). If I remove the name field from the DropdownItem, it infers correctly.

Actual behavior:
The type of item.id on line 19 is incorrectly identified as any, this causes our typescript-eslint no any rule to kick in.
Screenshot 2020-12-03 at 08 58 56

Playground Link: Example

Related Issues: I could find nothing that looked similar

@MartinJohns
Copy link
Contributor

Workaround:

const itemId = item.id;
if (item instanceof DropdownItem) {
	return [ itemId, item.name ];
} else {
	return [ itemId ];
}

Side-note:

Using instanceof on classes containing only public members is not a safe operation. Such classes a structurally typed, so an object literal is implicitly assignable, and in this case instanceof fails "unexpectedly".

@RyanCavanaugh RyanCavanaugh added the Bug A bug in TypeScript label Dec 3, 2020
@RyanCavanaugh RyanCavanaugh added this to the Backlog milestone Dec 3, 2020
@RyanCavanaugh
Copy link
Member

I think we should be using the constraint type to instantiate DropdownItem instead of any here

@yuvalbl
Copy link

yuvalbl commented Feb 6, 2022

Hey, any updates on this one?
I've encountered this issue as well - here is a simplified version of my scenario (which also using instanceof).

@MartinJohns I didn't understand your side-note, the following still fails even if I add a private field to MyClass

interface BaseT {
  baseProp: number;
}
export class MyClass<T extends BaseT> {
  propA: T;
}

const obj1 = {}; // simpified version, assume factory function which return type any

// should show a compilation error, since baseProp is number, but it doesn't!
// will show compilation error when "propA: T" by "propA: BaseT"
if (obj1 instanceof MyClass && obj1.propA.baseProp === 'aa') {
}

@kevincox
Copy link

This appears to be a duplicate of #17253

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript
Projects
None yet
Development

No branches or pull requests

5 participants