Typescript: Generic bounds not enforced on generic argument

Created on 10 Jul 2019  路  3Comments  路  Source: microsoft/TypeScript

TypeScript Version: [email protected], 3.5.1

Search Terms: generic bounds toString

Code

function f1<T>(o: T) {
  o.toString(); // error
  // This is expected, because T might be undefined or null
}

function f2<T extends {}>(o: T) {
  o.toString(); // no error
  // This is expected, this is the correct way to fix the above function.
}

function user<T>(t: T) {
  f1(t);  // Allowed, but irrelevant because f1() is invalid
  f2(t);  // Allowed?  <- this is the bug
  t.toString();  // Error, expected for the same reason as f1()
}

Expected behavior:
You should not be able to pass an arbitrary T into a function that demands T extends {}.

Actual behavior:
Despite .toString() being disallowed both in user() and in f1(), you can just pass it to f2() and still call toString on the same value.

Playground Link:
http://www.typescriptlang.org/play/#code/GYVwdgxgLglg9mABMAjAHgCoD4AUcBciGAlIgN4BQiicAdFHAMpQBOMYA5jsQNwUC+FCqEiwEyAEyZEAUwAeUGWAAmAZ3L9cBIqUrU6DZm07c+g4eGjwkIVTJaZcUQiXJVkKHFF7vgErz7UUPRMrOxcPuZAA

Related Issues:

Breaking Change Bug Fix Available Rescheduled

Most helpful comment

Is an old special assignability rule case left over from when the default for an unconstrained type parameter was {} instead of unknown, I believe. I think it's just a matter of deleting these lines:


                        if (!constraint || (source.flags & TypeFlags.TypeParameter && constraint.flags & TypeFlags.Any)) {
                            // A type variable with no constraint is not related to the non-primitive object type.
                            if (result = isRelatedTo(emptyObjectType, extractTypesOfKind(target, ~TypeFlags.NonPrimitive))) {
                                errorInfo = saveErrorInfo;
                                return result;
                            }
                        }

in checker.ts - but the impact of doing so will need to be checked, ofc.

All 3 comments

Perhaps a simpler expression of this is

function user<T>(t: T) {
  let o: {} = t;
}

Which I also expect should fail.

@weswigham ?

Is an old special assignability rule case left over from when the default for an unconstrained type parameter was {} instead of unknown, I believe. I think it's just a matter of deleting these lines:


                        if (!constraint || (source.flags & TypeFlags.TypeParameter && constraint.flags & TypeFlags.Any)) {
                            // A type variable with no constraint is not related to the non-primitive object type.
                            if (result = isRelatedTo(emptyObjectType, extractTypesOfKind(target, ~TypeFlags.NonPrimitive))) {
                                errorInfo = saveErrorInfo;
                                return result;
                            }
                        }

in checker.ts - but the impact of doing so will need to be checked, ofc.

Was this page helpful?
0 / 5 - 0 ratings