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

Type parameter explicitly constrained by unknown should not be assignable to {} #26796

Closed
mattmccutchen opened this issue Aug 30, 2018 · 5 comments
Assignees
Labels
Committed The team has roadmapped this issue Suggestion An idea for TypeScript

Comments

@mattmccutchen
Copy link
Contributor

mattmccutchen commented Aug 30, 2018

TypeScript Version: master (2deb318)

Search Terms: conditional type variable parameter undefined null empty object constraint

Code

// With --strictNullChecks
type Test1 = [unknown] extends [{}] ? true : false;  // false
type IsDefinitelyDefined<T extends unknown> = [T] extends [{}] ? true : false;
type Test2 = IsDefinitelyDefined<unknown>;  // true, should be false

function oops<T extends unknown>(arg: T): {} {
    return arg;  // no error, should be an error (unknown not assignable to {})
}
console.log(oops(null).constructor);  // bang!

Expected behavior / Actual behavior: as marked

Playground Link: link (remember to enable strictNullChecks)

Related Issues: didn't find any

Discovered via https://stackoverflow.com/questions/52105268 .

@ahejlsberg
Copy link
Member

ahejlsberg commented Aug 31, 2018

In the early days before --strictNullChecks, the {} type was our top type. With --strictNullChecks we carved out null and undefined and effectively our top type then became {} | null | undefined. However, for reasons of backward compatibility we kept {} as the default inference for an unconstrained type parameter and we kept the rule that allows an unconstrained type parameter to be assigned to {}.

We now have a proper top type called unknown and if it wasn't for backward compatibility we'd switch from {} to unknown in the situations above. However, it would be a significant breaking change.

@mattmccutchen
Copy link
Contributor Author

mattmccutchen commented Aug 31, 2018

I would happily take the breaking change (under a new strict flag) for the soundness; in fact, I assumed it had come as part of --strictNullChecks until I discovered this issue. (What was the specific reason that the change was unacceptable to make as part of --strictNullChecks, which was a large breaking change already?)

If you don't want to do that, we could at least make a type parameter explicitly constrained by unknown not assignable to {}; nobody should be relying on that behavior. If we do that and then people use a lint rule to require every type parameter in their code to have a constraint (either unknown, {}, or something else), then we'll be in pretty good shape; the standard library may still have legacy unconstrained type parameters, but it seems unlikely to me that any of them will expose the buggy behavior. Shall I write the PR for the assignability change?

@mattmccutchen mattmccutchen changed the title Conditional type assumes type variable is assignable to {} but it might be null/undefined Type parameter explicitly constrained by unknown should not be assignable to {} Aug 31, 2018
@RyanCavanaugh RyanCavanaugh added Suggestion An idea for TypeScript Committed The team has roadmapped this issue labels Sep 17, 2018
@RyanCavanaugh
Copy link
Member

Per notes in #26954, committed to trying it, at least

@ahejlsberg
Copy link
Member

The original issue here is actually not controversial. A type parameter with an explicit constraint of unknown shouldn't be assignable to {}. We should just fix that.

The larger issue is whether it is possible for us to switch the default constraint for type parameters to unknown as we discussed in #26954. We should run that experiment and discuss.

@NN---
Copy link

NN--- commented Jan 11, 2019

So, is it planned for next release ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Committed The team has roadmapped this issue Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

5 participants