Skip to content
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

Why I use function generic parameters as the other type's generic has compile error? #29225

Closed
kunlongxu opened this issue Jan 2, 2019 · 9 comments
Assignees
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed

Comments

@kunlongxu
Copy link

TypeScript Version: 3.3.0-dev.20190101

Search Terms: Why I use function generic parameters as the other type's generic has compile error?

Code

function fn1<U extends UU, UU>() {

    type T03<T> = {
      action: T;
    };
    type T04<F extends FF, FF> = T03<F> extends T03<FF> ? number : string;

    let v2: T04<"aaa", string> = 222;
    let v3: T04<U, UU> = 78787;
  }

Expected behavior:

 expect v3' type is number

Actual behavior:

Type '78787' is not assignable to type 'T04<U, UU>'.

Playground Link: Link

@Nathan-Fenner
Copy link
Contributor

Reduced example:

function example<Super, Sub extends Super>() {
    const shouldWork:  Sub extends Super ? number : string = 78787; // error: does not simplif
}

Type '78787' is not assignable to type 'Sub extends Super ? number : string'.

It seems that the conditional type gets stuck, even though it should simplify since we know that Sub extends Super from the generic constraint bound.

@DanielRosenwasser DanielRosenwasser added the Bug A bug in TypeScript label Jan 2, 2019
@DanielRosenwasser DanielRosenwasser added this to the TypeScript 3.3 milestone Jan 2, 2019
@jack-williams
Copy link
Collaborator

jack-williams commented Jan 2, 2019

I think the extends in a conditional type uses the definitely assignable relation, but I think type arguments only use assignable. I'm not sure it is always safe to reduce the conditional type because assignability does not imply definite assignability.

@fatcerberus
Copy link

assignability does not imply definite assignability

I wasn’t even aware this distinction existed; I’m kind of curious now, what’s the difference? (Preferably with examples)

@kunlongxu
Copy link
Author

@jack-williams @fatcerberus I have a another piece.I think is a similar question.

export interface Action<T = any> {
  type: T;
}
export interface AnyAction extends Action {
  [extraProps: string]: any;
}
export type Reducer<S = any, A extends Action = AnyAction> = (
  state: S | undefined,
  action: A
) => S;
export type ReducersMapObject<S extends any= any, A extends Action = Action> = {
  [K in keyof S]: Reducer<S[K], A>
};


function assertShape<S extends any, A extends Action>(
  reducers: ReducersMapObject<S, A>
) {
  let reducerKeys = Object.keys(reducers);
  reducerKeys.forEach(key => {
    let reducer = reducers[key];
    const SET_AGE="set age haha"
    const action={
      type:SET_AGE 
    }
   // Argument of type '{ type: string; }' is not assignable to parameter of type 'A'.
    reducer({sf:"rewr"}, action);
  });
}

@jack-williams
Copy link
Collaborator

@linmodev That looks a different situation to me, which I think is ok. The type of reducer is parameterised by A which is not controlled by the callee, but the caller of the function. It seems right that the body of the function should not be able to assume the action takes a string type. This use of the function type-checks:

const a: (state: "a" | undefined, action: Action<number>) => "a" = () => "a";
assertShape<{ a: "a" }, Action<number>>({ a });

@fatcerberus
This is from the code:

// Return trueType for a definitely true extends check. The definitely assignable relation excludes
// type variable constraints from consideration. Without the definitely assignable relation, the type
// type Foo<T extends { x: any }> = T extends { x: string } ? string : number
// would immediately resolve to 'string' instead of being deferred.

@kunlongxu
Copy link
Author

@jack-williams But the compile error just show the second parameter of reducer's type is type A.How to resolve this?

@jack-williams
Copy link
Collaborator

@linmodev I'm not sure what your use-case is, so I don't know how to fix the error. I believe the error message is correct; in the body of the function, the type A represents a fixed but unknown type, so it is not safe to assign to it using a concrete literal. The issue #29049 will produce a better error message though, which should help. Something like:

'{ type: string; }' is assignable to the constraint of type 'A', but 'A' could be instantiated with a different subtype of constraint '{ type: any; }'.

@kunlongxu
Copy link
Author

@jack-williams Thanks ,Have read the issue ,I find the key point of this eventually. I will continually pay a attention on it

@ahejlsberg
Copy link
Member

ahejlsberg commented Jan 5, 2019

This is a design limitation. The algorithm we use to determine whether to defer resolution of a conditional type doesn't consider constraints of type variables. Specifically, for a conditional type T extends U ? X : Y,

  • we resolve to Y when T is not assignable to U considering all type parameters referenced in T and U related (i.e. T is definitely not assignable to U),
  • otherwise we resolve to X when T is assignable to U considering all type parameters referenced in T and U unrelated (i.e. T is definitely assignable to U),
  • otherwise we defer resolution.

We use these rules because type parameters that appear unrelated, such as T extends { a: string } and U extends { b: string } might actually become related upon instantiation (e.g. when both are instantiated to { a: string, b: string }). Even so, there are some cases, such as type parameters constrained to other type parameters, where we could deduce that they are always related, but we currently don't reason about those. For that reason we may sometimes be too conservative in deciding when to resolve conditional types.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed
Projects
None yet
Development

No branches or pull requests

6 participants