Skip to content

this parameter of union signatures should be the intersection of constituents’ this parameters #31547

Closed
@andrewbranch

Description

@andrewbranch

...but currently it’s the union. Discovered while looking at #31485.

Minimal repro:

type F1 = (this: void) => void;
type F2 = (this: number) => void;
declare var fn: F1 | F2;
fn(); // should error

Silly but understandable real-world-ish example (playground):

interface Car {
    odometer: number;
    drive(): void;
}

interface Cow {
    weight: number;
    eatGrass(): void;
    isARealCow(): boolean;
}

function carWorks(this: Car): boolean {
    this.drive();
    return this.odometer > 0;
}

function cowWorks(this: Cow): boolean {
    const prevWeight = this.weight;
    this.eatGrass();
    return this.weight > prevWeight;
}

const cow = {
    weight: 100,
    eatGrass() { this.weight++ },
    // Conditional expression creates a union type here
    isARealCow: Math.random() > 0.5 ? carWorks : cowWorks
}

// Runtime error ~50% of the time:
// TypeError: this.drive is not a function
//    at Object.carWorks [as isARealCow]
cow.isARealCow();

Since cow.isARealCow might call this.drive() or might call this.eatGrass(), the only thing that should be able to call it is something has a weight, eatGrass(), odometer, and drive()—that is, the intersection of the constitutents’ this parameter types. (Perhaps a mechanical cow of some kind would do.)

TypeScript Version: 6a559e3

Search Terms: union signatures this parameter variance intersection

Playground Link

Related Issues: #31485

Metadata

Metadata

Assignees

Labels

BugA bug in TypeScriptFix AvailableA PR has been opened for this issue

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions