TypeScript icon indicating copy to clipboard operation
TypeScript copied to clipboard

get property return type inferred incorrectly from 'this' type, when other methods exist

Open martaver opened this issue 8 months ago β€’ 4 comments

πŸ”Ž Search Terms

get property get accessor incorrectly inferred type incorrectly inferred return type this ThisType

πŸ•— Version & Regression Information

This is the behaviour in every version I tried, and I reviewed the FAQ for entries about 'get accessors'

Before 4.8.4, even stand-alone 'get' properties didn't infer their type correctly.

⏯ Playground Link

https://www.typescriptlang.org/play/?ts=5.8.2#code/PQKhCgAIUhJSAWBDAbgU0kyAzArgOwGMAXASwHt9JjzJCkAHY3AJw2IXYE8GMADACp9I5bJirkARgCs0JSABM5AGyQskZSpBSkspfNjRsiaAHRQYAQWLVOkAM5IAtu1IuANHEgB3JPhs0kAws5DpKthh4RJpUWMQ8-AAKwhwaXvRU+rLyWhwYUtnEAOT2kEUcpPZF1Anm0MDg4MDAkADipOixDsQapIQ1vJCJ4FEkFFTB5Az2ADyJkGgAHsRo+AqlBXLEAHwAFACUkADeUJBnbMwsVCdnt7fNcAZGEQMYAiJifiIyW56TYfoAOZDTClIqTabVLSkYhgipVV6QfQrK5IZTKLh1O63SY0ABcOAIYy0MwEewA+rjyAT3gAySACBCVAQJUmQelHIIhaYE+YAX22hxu2POaEuVEpIUCSFKAlOIr58sgisVTTAFkgAHVOFR4EpCKo2JgyoCxdUIUZ4iIqHlvoVbGkLqx8KUsCg0bhIiEnC8sls0ApEXxkp5bU6rojKtAkU8WGxA4RyHGthi8RYGk0WizBqIypJVAhqlGivhcE5JEYiuZEy6bN4kwBrIGQAC8XKms05U2IBNL5eeAoOpipu2FZ1NNnzSAQB2OSru4ZtTPsw+5K+7Sr57nAfP2jVAEGjAAlyN40OgWJ5vDrMIRE04GH5SAHIJIuOJ3+Q8ixIC4OOR1lDOxF0RXM+CnBBhAre80FKPg-C4Pg03qfcswSD48wLap7AQchcGUQMKzKPsKxYIpPEkXAbGLBCq3AGt7EnEIG1WVt22mGYuyYXsy1I5U9n2Vcv3IUclWwcgRKFedbhAgBGTdtzuCdXwLWcx2xED4SE6ZTA3O4tx3fYgA

πŸ’» Code

/**
 * I have a function to capture the type `T` of an object declaration via inference.
 * At the same time, I want to provide the function a type `P` that I can inject on the object's 'this' type.
 */

// Given a static type P
function props<P extends object>() {
    return {
        // Infer the type T of an object, providing P as 'props' on its 'this' type internally.
        proto: function <T>(_proto: T & ThisType<T & { props: P }>) {
        return _proto as T
        }
    }
}

/**
 * When I declare a 'get' property on the object that returns a value from the injected type `P`, the return type is * inferred correctly:
 */

// Type of 'blah' is 'number'.
const working = props<{ opt: number }>().proto({
    get blah() {
        return this.props.opt
    },
})

/**
 * However, when accompanied by any other methods, the return type of `blah` becomes `any`:
 */

// Type of 'blah' should be 'number', but is 'any'.
const broken = props<{ opt: number }>().proto({
    foo() {
        return 1
    },
    get blah() {
        return this.props.opt
    },
})

πŸ™ Actual behavior

By itself, the 'blah' get accessor infers its return type from this.props.opt correctly, but when another method is added to the same object, its type changes to any.

πŸ™‚ Expected behavior

The inferred return type of blah get accessor should not be changed by adding a method to the object.

Additional information about the issue

No response

martaver avatar Mar 06 '25 07:03 martaver

The problem here is that getters are resolved eagerly even though they might have a context-sensitive dependency on this. It's not something that methods suffer from even though they might depend on this (TS playground):

const thisWorksFine = props<{ opt: number }>().proto({
  foo() {
    return 1;
  },
  blah() {
    return this.props.opt;
  },
});

That's because they are deferred by nature. So the return type of that blah method can be type-checked when this type is already more well-established.

A strong duplicate of https://github.com/microsoft/TypeScript/issues/47150 A duplicate (in slight disguise) of https://github.com/microsoft/TypeScript/issues/49511 Slightly related to https://github.com/microsoft/TypeScript/issues/58484

Andarist avatar Mar 06 '25 09:03 Andarist

What could be more well-established than a static type { opt: number }? :smiley:

Thanks for the links, I should have spent longer looking for dupes... do you want me to close this issue and track one of these others ones? Which would be the one to follow?

martaver avatar Mar 07 '25 00:03 martaver

Tbf, one of the first ones I linked to should be closed in the favor of the other. They look like duplicates to me - but it’s up to @RyanCavanaugh to decide. So I can’t tell which one you should follow. You can always follow both, they are not noisy anyway :p

Andarist avatar Mar 07 '25 06:03 Andarist

Omnibus tracked at #47599. An alternative workaround is to write

  foo: () => {
    return 1;
  },

RyanCavanaugh avatar Mar 19 '25 21:03 RyanCavanaugh

This issue has been marked as "Duplicate" and has seen no recent activity. It has been automatically closed for house-keeping purposes.

typescript-bot avatar Mar 22 '25 01:03 typescript-bot