Repository navigation
Case for inference failure in T extends F<T>Β #51377
Description
Activity
- addedSuggestionAn idea for TypeScriptAn idea for TypeScriptExperience EnhancementNoncontroversial enhancementsNoncontroversial enhancementsHelp WantedYou can do thisYou can do this
on Nov 1, 2022 In TypeScript, generics usually work just fine, however when you start using already extended generic types to define new generic types that will also be extended - this is where the trouble occurs. In general, Generic Constraints (e.g. ) in TypeScript are not considered as types that can be inferred further, meaning that whilst extending an extended type like you have in f4 is harmless, extending a newly defined type that also uses generics is problematic:
f4 is perfectly fine:
<Type1 extends Type2, Type2 extends OrderType>
f3 is also fine as it is not extending the type to define the callback but is instead creating directly:
f: (t: Type) => Promise
f2 is where the problem begins to occur as the parameter type is using an extended generic type that also uses extended values. This causes the type to default itself back to OrderType and because FullType does not directly equal OrderType you are observing that error:
Fn extends (t: Type) => Promise
Reacted by Homa WongBtw one can write a class equivalent of this too...
interface F { a: unknown , b: (a: this["a"]) => unknown } new class implements F { a = "hello" b = x => x.toUpperCase() } // doesn't compile because `x` is inferred as `any` instead of `string`. new class implements F { a = "hello" b = (x: string) => x.toUpperCase() } // compiles
Ryan Cavanaugh (@RyanCavanaugh) Could I use this thread to ask some quick trivial questions about the codebase? I'm giving a silly shot at writing a PR for this. The question I have right now is how do you create a type from a type alias symbol and a type passed in as an argument? I imagine something like this...?
let yType = createType(0) // What flags to pass in? yType.aliasSymbol = fSymbol yType.aliasArguments = [xType]
yType.__debugTypeToString()does give me"F<X>"but I'm not sure if this is the right way or if it works.Also let me know if there's a better place/way to ask questions like this.
Edit: I don't need the answer anymore turns out I didn't exactly needed it (see #51511), but still I'd be bonus if I get to know the answer anyway :P
Edit 2: Ah I think
getTypeAliasInstantiation(fSymbol, [xType])would do the trick.This would be helpful for excluding certain types in a generic type
T. For exampleOptional<null>doesn't really make sense so it would be nice if we could tell TypeScript that some types should not be allowed for generic typeTExample:
type Optional<T extends NonNullable<T>> = | { kind: 'Some'; value: T; } | { kind: 'None'; }; type OptionalNull = Optional<null>; // ^^^^ // Error: Type 'null' does not satisfy the constraint 'NonNullable<null>'. // Because type 'null' does not satisfy the constraint 'never'. type OptionalNull = Optional<null | string>; // ^^^^^^^^^^^^^ // Error: Type 'null | string' does not satisfy the constraint 'NonNullable<null | string>'. // Because type 'null' does not satisfy the constraint 'string'.
Your comment/usecase belongs to #51011.
This issue is for this specific inference failure in the OP, and here
T extends F<T>is already allowed because it's not immediately circular.
Bug Report
π Search Terms
circular type parameter constraint
Related issues: #40439 #30134
π Version & Regression Information
Tested with 4.8.4
β― Playground Link
Playground
π» Code
Note that there is a workaround but it's only a workaround for this minimal case and not for the real world case from which this minimal case was derived.
π Actual behavior
xin the firstfcall gets inferred asunknownπ Expected behavior
xin the firstfcall should get inferred asstring