Skip to content

Try making the apparent type of unknown be unknown #47517

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

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12355,7 +12355,7 @@ m2: ${(this.mapper2 as unknown as DebugTypeMapper).__debugToString().split("\n")
t.flags & TypeFlags.ESSymbolLike ? getGlobalESSymbolType() :
t.flags & TypeFlags.NonPrimitive ? emptyObjectType :
t.flags & TypeFlags.Index ? keyofConstraintType :
t.flags & TypeFlags.Unknown && !strictNullChecks ? emptyObjectType :
// t.flags & TypeFlags.Unknown && !strictNullChecks ? emptyObjectType :
t;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
tests/cases/conformance/types/typeRelationships/bestCommonType/bestCommonTypeOfConditionalExpressions.ts(25,5): error TS2322: Type 'T | U' is not assignable to type 'Object'.
Type 'T' is not assignable to type 'Object'.


==== tests/cases/conformance/types/typeRelationships/bestCommonType/bestCommonTypeOfConditionalExpressions.ts (1 errors) ====
// conditional expressions return the best common type of the branches plus contextual type (using the first candidate if multiple BCTs exist)
// no errors expected here

var a: { x: number; y?: number };
var b: { x: number; z?: number };

class Base { foo: string; }
class Derived extends Base { bar: string; }
class Derived2 extends Base { baz: string; }
var base: Base;
var derived: Derived;
var derived2: Derived2;

var r = true ? 1 : 2;
var r3 = true ? 1 : {};
var r4 = true ? a : b; // typeof a
var r5 = true ? b : a; // typeof b
var r6 = true ? (x: number) => { } : (x: Object) => { }; // returns number => void
var r7: (x: Object) => void = true ? (x: number) => { } : (x: Object) => { };
var r8 = true ? (x: Object) => { } : (x: number) => { }; // returns Object => void
var r10: Base = true ? derived : derived2; // no error since we use the contextual type in BCT
var r11 = true ? base : derived2;

function foo5<T, U>(t: T, u: U): Object {
return true ? t : u; // BCT is Object
~~~~~~~~~~~~~~~~~~~~
!!! error TS2322: Type 'T | U' is not assignable to type 'Object'.
!!! error TS2322: Type 'T' is not assignable to type 'Object'.
!!! related TS2208 tests/cases/conformance/types/typeRelationships/bestCommonType/bestCommonTypeOfConditionalExpressions.ts:24:15: This type parameter might need an `extends Object` constraint.
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
tests/cases/compiler/immutable.ts(261,76): error TS2344: Type 'T' does not satisfy the constraint 'Object'.
tests/cases/compiler/immutable.ts(341,22): error TS2430: Interface 'Keyed<K, V>' incorrectly extends interface 'Collection<K, V>'.
The types returned by 'toSeq()' are incompatible between these types.
Type 'Keyed<K, V>' is not assignable to type 'this'.
Expand Down Expand Up @@ -33,7 +34,7 @@ tests/cases/compiler/immutable.ts(391,22): error TS2430: Interface 'Set<T>' inco
flatMap<M>(mapper: (value: T, key: void, iter: this) => Ara<M>, context?: any): N2<M>;
toSeq(): N2<T>;
}
==== tests/cases/compiler/immutable.ts (3 errors) ====
==== tests/cases/compiler/immutable.ts (4 errors) ====
// Test that complex recursive collections can pass the `extends` assignability check without
// running out of memory. This bug was exposed in Typescript 2.4 when more generic signatures
// started being checked.
Expand Down Expand Up @@ -295,6 +296,9 @@ tests/cases/compiler/immutable.ts(391,22): error TS2430: Interface 'Set<T>' inco
}
}
export function Record<T>(defaultValues: T, name?: string): Record.Class<T>;
~
!!! error TS2344: Type 'T' does not satisfy the constraint 'Object'.
!!! related TS2208 tests/cases/compiler/immutable.ts:261:26: This type parameter might need an `extends Object` constraint.
export module Seq {
function isSeq(maybeSeq: any): maybeSeq is Seq.Indexed<any> | Seq.Keyed<any, any>;
function of<T>(...values: Array<T>): Seq.Indexed<T>;
Expand Down
134 changes: 134 additions & 0 deletions tests/baselines/reference/conditionalTypeDoesntSpinForever.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
tests/cases/compiler/conditionalTypeDoesntSpinForever.ts(96,41): error TS2769: No overload matches this call.
Overload 1 of 2, '(o: {}): string[]', gave the following error.
Argument of type 'SO_FAR' is not assignable to parameter of type '{}'.
Overload 2 of 2, '(o: object): string[]', gave the following error.
Argument of type 'SO_FAR' is not assignable to parameter of type 'object'.


==== tests/cases/compiler/conditionalTypeDoesntSpinForever.ts (1 errors) ====
// A *self-contained* demonstration of the problem follows...
// Test this by running `tsc --target es6` on the command-line, rather than through another build tool such as Gulp, Webpack, etc.

export enum PubSubRecordIsStoredInRedisAsA {
redisHash = "redisHash",
jsonEncodedRedisString = "jsonEncodedRedisString"
}

export interface PubSubRecord<NAME extends string, RECORD, IDENTIFIER extends Partial<RECORD>> {
name: NAME;
record: RECORD;
identifier: IDENTIFIER;
storedAs: PubSubRecordIsStoredInRedisAsA;
maxMsToWaitBeforePublishing: number;
}

type NameFieldConstructor<SO_FAR> =
SO_FAR extends {name: any} ? {} : {
name: <TYPE>(t?: TYPE) => BuildPubSubRecordType<SO_FAR & {name: TYPE}>
}

const buildNameFieldConstructor = <SO_FAR>(soFar: SO_FAR) => (
"name" in soFar ? {} : {
name: <TYPE>(instance: TYPE = undefined) =>
buildPubSubRecordType(Object.assign({}, soFar, {name: instance as TYPE}) as SO_FAR & {name: TYPE}) as BuildPubSubRecordType<SO_FAR & {name: TYPE}>
}
);

type StoredAsConstructor<SO_FAR> =
SO_FAR extends {storedAs: any} ? {} : {
storedAsJsonEncodedRedisString: () => BuildPubSubRecordType<SO_FAR & {storedAs: PubSubRecordIsStoredInRedisAsA.jsonEncodedRedisString}>;
storedRedisHash: () => BuildPubSubRecordType<SO_FAR & {storedAs: PubSubRecordIsStoredInRedisAsA.redisHash}>;
}

const buildStoredAsConstructor = <SO_FAR>(soFar: SO_FAR) => (
"storedAs" in soFar ? {} : {
storedAsJsonEncodedRedisString: () =>
buildPubSubRecordType(Object.assign({}, soFar, {storedAs: PubSubRecordIsStoredInRedisAsA.jsonEncodedRedisString})) as
BuildPubSubRecordType<SO_FAR & {storedAs: PubSubRecordIsStoredInRedisAsA.jsonEncodedRedisString}>,
storedAsRedisHash: () =>
buildPubSubRecordType(Object.assign({}, soFar, {storedAs: PubSubRecordIsStoredInRedisAsA.redisHash})) as
BuildPubSubRecordType<SO_FAR & {storedAs: PubSubRecordIsStoredInRedisAsA.redisHash}>,
}
);

type IdentifierFieldConstructor<SO_FAR> =
SO_FAR extends {identifier: any} ? {} :
SO_FAR extends {record: any} ? {
identifier: <TYPE extends Partial<SO_FAR["record"]>>(t?: TYPE) => BuildPubSubRecordType<SO_FAR & {identifier: TYPE}>
} : {}

const buildIdentifierFieldConstructor = <SO_FAR>(soFar: SO_FAR) => (
"identifier" in soFar || (!("record" in soFar)) ? {} : {
identifier: <TYPE>(instance: TYPE = undefined) =>
buildPubSubRecordType(Object.assign({}, soFar, {identifier: instance as TYPE}) as SO_FAR & {identifier: TYPE}) as BuildPubSubRecordType<SO_FAR & {identifier: TYPE}>
}
);

type RecordFieldConstructor<SO_FAR> =
SO_FAR extends {record: any} ? {} : {
record: <TYPE>(t?: TYPE) => BuildPubSubRecordType<SO_FAR & {record: TYPE}>
}

const buildRecordFieldConstructor = <SO_FAR>(soFar: SO_FAR) => (
"record" in soFar ? {} : {
record: <TYPE>(instance: TYPE = undefined) =>
buildPubSubRecordType(Object.assign({}, soFar, {record: instance as TYPE}) as SO_FAR & {record: TYPE}) as BuildPubSubRecordType<SO_FAR & {record: TYPE}>
}
);

type MaxMsToWaitBeforePublishingFieldConstructor<SO_FAR> =
SO_FAR extends {maxMsToWaitBeforePublishing: any} ? {} : {
maxMsToWaitBeforePublishing: (t: number) => BuildPubSubRecordType<SO_FAR & {maxMsToWaitBeforePublishing: number}>,
neverDelayPublishing: () => BuildPubSubRecordType<SO_FAR & {maxMsToWaitBeforePublishing: 0}>,
}

const buildMaxMsToWaitBeforePublishingFieldConstructor = <SO_FAR>(soFar: SO_FAR): MaxMsToWaitBeforePublishingFieldConstructor<SO_FAR> => (
"maxMsToWaitBeforePublishing" in soFar ? {} : {
maxMsToWaitBeforePublishing: (instance: number = 0) =>
buildPubSubRecordType(Object.assign({}, soFar, {maxMsToWaitBeforePublishing: instance})) as BuildPubSubRecordType<SO_FAR & {maxMsToWaitBeforePublishing: number}>,
neverDelayPublishing: () =>
buildPubSubRecordType(Object.assign({}, soFar, {maxMsToWaitBeforePublishing: 0})) as BuildPubSubRecordType<SO_FAR & {maxMsToWaitBeforePublishing: 0}>,
}
) as MaxMsToWaitBeforePublishingFieldConstructor<SO_FAR>;

type TypeConstructor<SO_FAR> =
SO_FAR extends {identifier: any, record: any, maxMsToWaitBeforePublishing: number, storedAs: PubSubRecordIsStoredInRedisAsA} ? {
type: SO_FAR,
fields: Set<keyof SO_FAR>,
hasField: (fieldName: string | number | symbol) => fieldName is keyof SO_FAR
} : {}

const buildType = <SO_FAR>(soFar: SO_FAR) => (
"identifier" in soFar && "object" in soFar && "maxMsToWaitBeforePublishing" in soFar && "PubSubRecordIsStoredInRedisAsA" in soFar ? {} : {
type: soFar,
fields: () => new Set(Object.keys(soFar) as (keyof SO_FAR)[]),
~~~~~
!!! error TS2769: No overload matches this call.
!!! error TS2769: Overload 1 of 2, '(o: {}): string[]', gave the following error.
!!! error TS2769: Argument of type 'SO_FAR' is not assignable to parameter of type '{}'.
!!! error TS2769: Overload 2 of 2, '(o: object): string[]', gave the following error.
!!! error TS2769: Argument of type 'SO_FAR' is not assignable to parameter of type 'object'.
!!! related TS2208 tests/cases/compiler/conditionalTypeDoesntSpinForever.ts:93:22: This type parameter might need an `extends {}` constraint.
!!! related TS2208 tests/cases/compiler/conditionalTypeDoesntSpinForever.ts:93:22: This type parameter might need an `extends object` constraint.
hasField: (fieldName: string | number | symbol) => fieldName in soFar
}
);

type BuildPubSubRecordType<SO_FAR> =
NameFieldConstructor<SO_FAR> &
IdentifierFieldConstructor<SO_FAR> &
RecordFieldConstructor<SO_FAR> &
StoredAsConstructor<SO_FAR> & // infinite loop goes away when you comment out this line
MaxMsToWaitBeforePublishingFieldConstructor<SO_FAR> &
TypeConstructor<SO_FAR>

const buildPubSubRecordType = <SO_FAR>(soFar: SO_FAR) => Object.assign(
{},
buildNameFieldConstructor(soFar),
buildIdentifierFieldConstructor(soFar),
buildRecordFieldConstructor(soFar),
buildStoredAsConstructor(soFar),
buildMaxMsToWaitBeforePublishingFieldConstructor(soFar),
buildType(soFar)
) as BuildPubSubRecordType<SO_FAR>;
const PubSubRecordType = buildPubSubRecordType({});
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
tests/cases/compiler/constructorReturningAPrimitive.ts(15,9): error TS2322: Type 'T' is not assignable to type 'B<T>'.
tests/cases/compiler/constructorReturningAPrimitive.ts(15,9): error TS2409: Return type of constructor signature must be assignable to the instance type of the class.


==== tests/cases/compiler/constructorReturningAPrimitive.ts (2 errors) ====
// technically not allowed by JavaScript but we don't have a 'not-primitive' constraint
// functionally only possible when your class is otherwise devoid of members so of little consequence in practice

class A {
constructor() {
return 1;
}
}

var a = new A();

class B<T> {
constructor() {
var x: T;
return x;
~~~~~~~~~
!!! error TS2322: Type 'T' is not assignable to type 'B<T>'.
!!! related TS2208 tests/cases/compiler/constructorReturningAPrimitive.ts:12:9: This type parameter might need an `extends B<T>` constraint.
~~~~~~~~~
!!! error TS2409: Return type of constructor signature must be assignable to the instance type of the class.
}
}

var b = new B<number>();
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithObjectTypeArgsAndIndexersErrors.ts(10,9): error TS2413: 'number' index type 'T' is not assignable to 'string' index type 'Object'.


==== tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithObjectTypeArgsAndIndexersErrors.ts (1 errors) ====
// Type inference infers from indexers in target type, error cases

function foo<T>(x: T) {
return x;
}

function other<T>(arg: T) {
var b: {
[x: string]: Object;
[x: number]: T; // ok, T is a subtype of Object because its apparent type is {}
~~~~~~~~~~~~~~~
!!! error TS2413: 'number' index type 'T' is not assignable to 'string' index type 'Object'.
};
var r2 = foo(b); // T
}

function other3<T extends U, U extends Date>(arg: T) {
var b: {
[x: string]: Object;
[x: number]: T;
};
var r2 = foo(b);
var d = r2[1];
var e = r2['1'];
var u: U = r2[1]; // ok
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ tests/cases/compiler/genericConditionalConstrainedToUnknownNotAssignableToConcre
Type 'unknown' is not assignable to type 'A'.
Type 'ReturnType<FunctionsObj<T>[string]>' is not assignable to type 'A'.
Type 'unknown' is not assignable to type 'A'.
Property 'x' is missing in type '{}' but required in type 'A'.
Type 'unknown' is not assignable to type 'A'.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's very odd that this prints out twice.



==== tests/cases/compiler/genericConditionalConstrainedToUnknownNotAssignableToConcreteObject.ts (1 errors) ====
Expand Down Expand Up @@ -38,8 +38,7 @@ tests/cases/compiler/genericConditionalConstrainedToUnknownNotAssignableToConcre
!!! error TS2322: Type 'unknown' is not assignable to type 'A'.
!!! error TS2322: Type 'ReturnType<FunctionsObj<T>[string]>' is not assignable to type 'A'.
!!! error TS2322: Type 'unknown' is not assignable to type 'A'.
!!! error TS2322: Property 'x' is missing in type '{}' but required in type 'A'.
!!! related TS2728 tests/cases/compiler/genericConditionalConstrainedToUnknownNotAssignableToConcreteObject.ts:1:15: 'x' is declared here.
!!! error TS2322: Type 'unknown' is not assignable to type 'A'.
}

// Original CFA report of the above issue
Expand Down
23 changes: 23 additions & 0 deletions tests/baselines/reference/genericPrototypeProperty3.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
tests/cases/compiler/genericPrototypeProperty3.ts(6,5): error TS2416: Property 'target' in type 'MyEvent<T>' is not assignable to the same property in base type 'BaseEvent'.
Type 'T' is not assignable to type '{}'.


==== tests/cases/compiler/genericPrototypeProperty3.ts (1 errors) ====
class BaseEvent {
target: {};
}

class MyEvent<T> extends BaseEvent { // T is instantiated to any in the prototype, which is assignable to {}
target: T;
~~~~~~
!!! error TS2416: Property 'target' in type 'MyEvent<T>' is not assignable to the same property in base type 'BaseEvent'.
!!! error TS2416: Type 'T' is not assignable to type '{}'.
!!! related TS2208 tests/cases/compiler/genericPrototypeProperty3.ts:5:15: This type parameter might need an `extends {}` constraint.
}
class BaseEventWrapper {
t: BaseEvent;
}

class MyEventWrapper extends BaseEventWrapper {
t: MyEvent<any>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
tests/cases/conformance/expressions/binaryOperators/logicalOrOperator/logicalOrOperatorWithTypeParameters.ts(5,9): error TS2322: Type 'T | U' is not assignable to type '{}'.
Type 'T' is not assignable to type '{}'.
tests/cases/conformance/expressions/binaryOperators/logicalOrOperator/logicalOrOperatorWithTypeParameters.ts(14,9): error TS2322: Type 'U | V' is not assignable to type '{}'.
Type 'U' is not assignable to type '{}'.


==== tests/cases/conformance/expressions/binaryOperators/logicalOrOperator/logicalOrOperatorWithTypeParameters.ts (2 errors) ====
function fn1<T, U>(t: T, u: U) {
var r1 = t || t;
var r2: T = t || t;
var r3 = t || u;
var r4: {} = t || u;
~~
!!! error TS2322: Type 'T | U' is not assignable to type '{}'.
!!! error TS2322: Type 'T' is not assignable to type '{}'.
!!! related TS2208 tests/cases/conformance/expressions/binaryOperators/logicalOrOperator/logicalOrOperatorWithTypeParameters.ts:1:14: This type parameter might need an `extends {}` constraint.
}

function fn2<T, U/* extends T*/, V/* extends T*/>(t: T, u: U, v: V) {
var r1 = t || u;
//var r2: T = t || u;
var r3 = u || u;
var r4: U = u || u;
var r5 = u || v;
var r6: {} = u || v;
~~
!!! error TS2322: Type 'U | V' is not assignable to type '{}'.
!!! error TS2322: Type 'U' is not assignable to type '{}'.
!!! related TS2208 tests/cases/conformance/expressions/binaryOperators/logicalOrOperator/logicalOrOperatorWithTypeParameters.ts:8:17: This type parameter might need an `extends {}` constraint.
//var r7: T = u || v;
}

function fn3<T extends { a: string; b: string }, U extends { a: string; b: number }>(t: T, u: U) {
var r1 = t || u;
var r2: {} = t || u;
var r3 = t || { a: '' };
var r4: { a: string } = t || u;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
tests/cases/compiler/privateFieldAssignabilityFromUnknown.ts(2,3): error TS18028: Private identifiers are only available when targeting ECMAScript 2015 and higher.
tests/cases/compiler/privateFieldAssignabilityFromUnknown.ts(5,7): error TS2741: Property '#field' is missing in type '{}' but required in type 'Class'.
tests/cases/compiler/privateFieldAssignabilityFromUnknown.ts(5,7): error TS2322: Type 'unknown' is not assignable to type 'Class'.


==== tests/cases/compiler/privateFieldAssignabilityFromUnknown.ts (2 errors) ====
Expand All @@ -11,6 +11,5 @@ tests/cases/compiler/privateFieldAssignabilityFromUnknown.ts(5,7): error TS2741:

const task: Class = {} as unknown;
~~~~
!!! error TS2741: Property '#field' is missing in type '{}' but required in type 'Class'.
!!! related TS2728 tests/cases/compiler/privateFieldAssignabilityFromUnknown.ts:2:3: '#field' is declared here.
!!! error TS2322: Type 'unknown' is not assignable to type 'Class'.

Loading