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

Constraints applied by conditional types not considered when checking signatures #22347

Closed
weswigham opened this issue Mar 6, 2018 · 7 comments
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@weswigham
Copy link
Member

Code

declare function log(msg: string): void;
type EQString<T> = T extends string ? T : never;
function c<T>(x: T | string) {
    const y = x as EQString<T>; // assert stringiness
    return log(y);
}

Expected behavior:
No error.

Actual behavior:

[ts]
Argument of type 'EQString<T>' is not assignable to parameter of type 'string'.
  Type 'T' is not assignable to type 'string'

on y.

@weswigham weswigham changed the title Constraints applied by conditional types not considered when checking signatures Constraints applied by conditional types not considered later in control flow Mar 6, 2018
@weswigham weswigham changed the title Constraints applied by conditional types not considered later in control flow Constraints applied by conditional types not considered when checking signatures Mar 6, 2018
@weswigham weswigham added the Bug A bug in TypeScript label Mar 6, 2018
@ahejlsberg
Copy link
Member

This is actually working as intended. The constraint added to T by the T extends string check is in effect only in the true branch of the conditional type. The T produced by EqString<T> is exactly the same T as was passed in and it doesn't have any new constraints. To get the desired effect you need to return an intersection type:

type EQString<T> = T extends string ? T & string : never;

@ahejlsberg ahejlsberg added Working as Intended The behavior described is the intended behavior; this is not a bug and removed Bug A bug in TypeScript labels Mar 6, 2018
@weswigham
Copy link
Member Author

Hm. OK. That's not the greatest in the context of using conditionals for control flow narrowing, since it causes you to get intersections instead of constrained generics like you'd expect.

@kpdonn
Copy link
Contributor

kpdonn commented Mar 7, 2018

@weswigham You very well might already know this, but another way to get your example to work without the intersection is

declare function log(msg: string): void
type MustBeString<T extends string> = T
type EQString<T> = T extends MustBeString<infer U> ? U : never
function c<T>(x: T | string) {
  const y = x as EQString<T> // assert stringiness
  return log(y)
}

@weswigham
Copy link
Member Author

It's a little verbose but it seems like it checks out.

@ahejlsberg ahejlsberg removed their assignment Mar 7, 2018
@weswigham
Copy link
Member Author

weswigham commented Mar 9, 2018

Erm, doesn't check out too well, in heavy use practice. It may give the result a new constraint on whatever you say, but it forgets its relationship with the original T, which is bad:

declare function log(msg: string): void
type MustBeString<T extends string> = T;
type EQString<T> = T extends MustBeString<infer U> ? U : never
function c<T>(x: T | string) {
  const y = x as EQString<T> // assert stringiness
  return useStringyT(y); /// Noooooooo: Argument of type 'EQString<T>' is not assignable to parameter of type 'T & string'

  function useStringyT(x: T & string) {
      return 0;
  }
}

@weswigham
Copy link
Member Author

sigh just intersecting doesn't quite work either, since, for example, in concrete types ModuleKind.None & number doesn't simplify back to ModuleKind.None.

Replacing the constraint via an infer and then intersecting the original T with the inference kinda works.

@typescript-bot
Copy link
Collaborator

Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.

@microsoft microsoft locked and limited conversation to collaborators Jul 25, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

4 participants