Repository navigation
Type parameter's function properties referencing other properties of the same parameter are brokenΒ #60848
Copy link
Copy link
Closed as not planned
Closed as not planned
Copy link
Labels
DuplicateAn existing issue was already createdAn existing issue was already created
Description
π Search Terms
generics, type parameters, dependent properties
π Version & Regression Information
None of the versions I have tried work as expected.
β― Playground Link
π» Code
namespace round_one {
// let's start with declaring a function that:
// - accepts a numeric `primaryProperty`
// - accepts `dependentProperty` which should be identical to `primaryProperty`
// - accepts optional `dependentFunctionProperty` which is a function with a parameter based on `primaryProperty`
declare function define<const obj extends {
readonly primaryProperty: number
dependentProperty: obj['primaryProperty']
dependentFunctionProperty?: (primary: obj['primaryProperty']) => void
}>(obj: obj): obj
// so far everything works as expected
define( {
primaryProperty: 11,
dependentProperty: 11, // no errors
} )
define( {
primaryProperty: 11,
dependentProperty: 13, // an error
} )
define( {
primaryProperty: 11,
dependentProperty: 11,
dependentFunctionProperty: () => {}, // no errors if there is no parameter
} )
// but when we add a parameter to `dependentFunctionProperty`, it breaks because...
define( {
primaryProperty: 11,
dependentProperty: 11,
// Type '(primary: number) => void' is not assignable to type '(primary: unknown) => void'.
// though we didn't state which type we expect in the parameter! and why did `primary` become `unknown`?
dependentFunctionProperty: (primary) => {
primary
},
} )
}
namespace round_two {
// if we replace a function property with a method, things get even weirder.
declare function define<const obj extends {
readonly primaryProperty: number
dependentProperty: obj['primaryProperty']
dependentMethod(primary: obj['primaryProperty']): void
}>(obj: obj): obj
define( {
primaryProperty: 11,
dependentProperty: 11,
dependentMethod(primary) {
// `primary` is not inferred as `unknown` now, but still is not `11`: it's a `number`
primary
},
} )
// but we can manually annotate `primary` without any problems
// which would be impossible for "normal" function signatures expecting `number`
define( {
primaryProperty: 11,
dependentProperty: 11,
dependentMethod(primary: 11) { // no errors
primary
},
} )
// annotating `primary` with something different than 11 results in an error as well
define( {
primaryProperty: 11,
dependentProperty: 11,
dependentMethod(primary: 12) { // an error
primary
},
} )
}
namespace round_three {
// now we get to the weirdest part of the problem.
// accidentally i've discovered that simply repeating
// the declaration of `define` makes typescript behave almost correctly
declare function define<const obj extends {
readonly primaryProperty: number
dependentProperty: obj['primaryProperty']
dependentFunctionProperty?: (primary: obj['primaryProperty']) => void
}>(obj: obj): obj
declare function define<const obj extends {
readonly primaryProperty: number
dependentProperty: obj['primaryProperty']
dependentFunctionProperty?: (primary: obj['primaryProperty']) => void
}>(obj: obj): obj
define( {
primaryProperty: 11,
dependentProperty: 11,
// no errors here, but hovering over `primary` shows `number` as if we were declaring `dependentFunctionProperty` as a method
dependentFunctionProperty: (primary) => {
primary
},
} )
// annotating `primary` works like in `dependentMethod` as well
define( {
primaryProperty: 11,
dependentProperty: 11,
dependentFunctionProperty: (primary: 11) => { // no errors
primary
},
} )
define( {
primaryProperty: 11,
dependentProperty: 11,
dependentFunctionProperty: (primary: 12) => { // an error
primary
},
} )
}π Actual behavior
I'm not sure where to begin. Properties of a type parameter that reference other properties of the same type parameter seem to be completely untested in TypeScript.
In the example above, I have referenced a generic's property from other properties in three ways:
- from a function property
- from a method
- from a function property, but declaration of the function is duplicated!
All of them are buggy, but in different ways.
π Expected behavior
Speaking honestly, I was expecting from such dependent properties to not work at all, failing with some kind of circular constraint error, but now I believe they could become usable some day.
Additional information about the issue
No response
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
DuplicateAn existing issue was already createdAn existing issue was already created
Activity
Andarist commentedon Dec 24, 2024
TS inference algorithm can't always unify everything in ways you'd like it to when some information is missing. It has to deduce it after all and sometimes it might fail. That's why you see different results when you annotate that parameter vs when you don't. With annotated parameters, TS is able to infer some things earlier - in a single inference pass.
I'm surprised by the different behavior of your arrow function vs method examples. Both are context-sensitive and I'd expect them to behave roughly the same way in this context.
That said, your first example can be fixed if you use a reverse-mapped type (TS playground):
Andarist commentedon Dec 24, 2024
Ah, the method variant typechecks in ur case only because its typed using a method syntax and methods are bivariant
RyanCavanaugh commentedon Jan 2, 2025
Seems like duplicate of either #47599 or #30134. Unification is extremely unlikely to happen IMO (it can lead to runaway inferenecs); #47599 has some edge cases that might get polished in the future.
typescript-bot commentedon Jan 5, 2025
This issue has been marked as "Duplicate" and has seen no recent activity. It has been automatically closed for house-keeping purposes.