Skip to content

Use identity with the permissive instantation to detect nongenric instances and disable variance probing on nongeneric instances #29981

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

Merged
Merged
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
16 changes: 13 additions & 3 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12822,20 +12822,30 @@ namespace ts {
}
return Ternary.False;

function isNonGeneric(type: Type) {
// If we're already in identity relationship checking, we should use `isRelatedTo`
// to catch the `Maybe` from an excessively deep type (which we then assume means
// that the type could possibly contain a generic)
if (relation === identityRelation) {
return isRelatedTo(type, getPermissiveInstantiation(type)) === Ternary.True;
}
return isTypeIdenticalTo(type, getPermissiveInstantiation(type));
}

function relateVariances(sourceTypeArguments: ReadonlyArray<Type> | undefined, targetTypeArguments: ReadonlyArray<Type> | undefined, variances: Variance[]) {
if (result = typeArgumentsRelatedTo(sourceTypeArguments, targetTypeArguments, variances, reportErrors)) {
return result;
}
const isCovariantVoid = targetTypeArguments && hasCovariantVoidArgument(targetTypeArguments, variances);
varianceCheckFailed = !isCovariantVoid;
const allowStructuralFallback = (targetTypeArguments && hasCovariantVoidArgument(targetTypeArguments, variances)) || isNonGeneric(source) || isNonGeneric(target);
varianceCheckFailed = !allowStructuralFallback;
// The type arguments did not relate appropriately, but it may be because we have no variance
// information (in which case typeArgumentsRelatedTo defaulted to covariance for all type
// arguments). It might also be the case that the target type has a 'void' type argument for
// a covariant type parameter that is only used in return positions within the generic type
// (in which case any type argument is permitted on the source side). In those cases we proceed
// with a structural comparison. Otherwise, we know for certain the instantiations aren't
// related and we can return here.
if (variances !== emptyArray && !isCovariantVoid) {
if (variances !== emptyArray && !allowStructuralFallback) {
// In some cases generic types that are covariant in regular type checking mode become
// invariant in --strictFunctionTypes mode because one or more type parameters are used in
// both co- and contravariant positions. In order to make it easier to diagnose *why* such
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ const wat: Runtype<any> = Num;
>Num : Num

const Foo = Obj({ foo: Num })
>Foo : any
>Obj({ foo: Num }) : any
>Foo : Obj<{ foo: Num; }>
>Obj({ foo: Num }) : Obj<{ foo: Num; }>
>Obj : <O extends { [_: string]: Runtype<any>; }>(fields: O) => Obj<O>
>{ foo: Num } : { foo: Num; }
>foo : Num
Expand Down
4 changes: 0 additions & 4 deletions tests/baselines/reference/mappedTypeRelationships.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,7 @@ tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(66,5): error TS2
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(66,5): error TS2542: Index signature in type 'Readonly<U>' only permits reading.
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(72,5): error TS2322: Type 'Partial<T>' is not assignable to type 'T'.
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(78,5): error TS2322: Type 'Partial<Thing>' is not assignable to type 'Partial<T>'.
Type 'Thing' is not assignable to type 'T'.
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(88,5): error TS2322: Type 'Readonly<Thing>' is not assignable to type 'Readonly<T>'.
Type 'Thing' is not assignable to type 'T'.
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(127,5): error TS2322: Type 'Partial<U>' is not assignable to type 'Identity<U>'.
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(143,5): error TS2322: Type '{ [P in keyof T]: T[P]; }' is not assignable to type '{ [P in keyof T]: U[P]; }'.
Type 'T[P]' is not assignable to type 'U[P]'.
Expand Down Expand Up @@ -199,7 +197,6 @@ tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(168,5): error TS
y = x; // Error
~
!!! error TS2322: Type 'Partial<Thing>' is not assignable to type 'Partial<T>'.
!!! error TS2322: Type 'Thing' is not assignable to type 'T'.
}

function f40<T>(x: T, y: Readonly<T>) {
Expand All @@ -212,7 +209,6 @@ tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(168,5): error TS
y = x; // Error
~
!!! error TS2322: Type 'Readonly<Thing>' is not assignable to type 'Readonly<T>'.
!!! error TS2322: Type 'Thing' is not assignable to type 'T'.
}

type Item = {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//// [nongenericPartialInstantiationsRelatedInBothDirections.ts]
interface Foo {
a: number;
b: number;
bar: string;
}
interface ObjectContaining<T> {
new (sample: Partial<T>): Partial<T>
}
declare let cafoo: ObjectContaining<{ a: number, foo: number }>;
declare let cfoo: ObjectContaining<Foo>;
cfoo = cafoo;
cafoo = cfoo;


//// [nongenericPartialInstantiationsRelatedInBothDirections.js]
cfoo = cafoo;
cafoo = cfoo;
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
=== tests/cases/compiler/nongenericPartialInstantiationsRelatedInBothDirections.ts ===
interface Foo {
>Foo : Symbol(Foo, Decl(nongenericPartialInstantiationsRelatedInBothDirections.ts, 0, 0))

a: number;
>a : Symbol(Foo.a, Decl(nongenericPartialInstantiationsRelatedInBothDirections.ts, 0, 15))

b: number;
>b : Symbol(Foo.b, Decl(nongenericPartialInstantiationsRelatedInBothDirections.ts, 1, 14))

bar: string;
>bar : Symbol(Foo.bar, Decl(nongenericPartialInstantiationsRelatedInBothDirections.ts, 2, 14))
}
interface ObjectContaining<T> {
>ObjectContaining : Symbol(ObjectContaining, Decl(nongenericPartialInstantiationsRelatedInBothDirections.ts, 4, 1))
>T : Symbol(T, Decl(nongenericPartialInstantiationsRelatedInBothDirections.ts, 5, 27))

new (sample: Partial<T>): Partial<T>
>sample : Symbol(sample, Decl(nongenericPartialInstantiationsRelatedInBothDirections.ts, 6, 7))
>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(nongenericPartialInstantiationsRelatedInBothDirections.ts, 5, 27))
>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(nongenericPartialInstantiationsRelatedInBothDirections.ts, 5, 27))
}
declare let cafoo: ObjectContaining<{ a: number, foo: number }>;
>cafoo : Symbol(cafoo, Decl(nongenericPartialInstantiationsRelatedInBothDirections.ts, 8, 11))
>ObjectContaining : Symbol(ObjectContaining, Decl(nongenericPartialInstantiationsRelatedInBothDirections.ts, 4, 1))
>a : Symbol(a, Decl(nongenericPartialInstantiationsRelatedInBothDirections.ts, 8, 37))
>foo : Symbol(foo, Decl(nongenericPartialInstantiationsRelatedInBothDirections.ts, 8, 48))

declare let cfoo: ObjectContaining<Foo>;
>cfoo : Symbol(cfoo, Decl(nongenericPartialInstantiationsRelatedInBothDirections.ts, 9, 11))
>ObjectContaining : Symbol(ObjectContaining, Decl(nongenericPartialInstantiationsRelatedInBothDirections.ts, 4, 1))
>Foo : Symbol(Foo, Decl(nongenericPartialInstantiationsRelatedInBothDirections.ts, 0, 0))

cfoo = cafoo;
>cfoo : Symbol(cfoo, Decl(nongenericPartialInstantiationsRelatedInBothDirections.ts, 9, 11))
>cafoo : Symbol(cafoo, Decl(nongenericPartialInstantiationsRelatedInBothDirections.ts, 8, 11))

cafoo = cfoo;
>cafoo : Symbol(cafoo, Decl(nongenericPartialInstantiationsRelatedInBothDirections.ts, 8, 11))
>cfoo : Symbol(cfoo, Decl(nongenericPartialInstantiationsRelatedInBothDirections.ts, 9, 11))

Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
=== tests/cases/compiler/nongenericPartialInstantiationsRelatedInBothDirections.ts ===
interface Foo {
a: number;
>a : number

b: number;
>b : number

bar: string;
>bar : string
}
interface ObjectContaining<T> {
new (sample: Partial<T>): Partial<T>
>sample : Partial<T>
}
declare let cafoo: ObjectContaining<{ a: number, foo: number }>;
>cafoo : ObjectContaining<{ a: number; foo: number; }>
>a : number
>foo : number

declare let cfoo: ObjectContaining<Foo>;
>cfoo : ObjectContaining<Foo>

cfoo = cafoo;
>cfoo = cafoo : ObjectContaining<{ a: number; foo: number; }>
>cfoo : ObjectContaining<Foo>
>cafoo : ObjectContaining<{ a: number; foo: number; }>

cafoo = cfoo;
>cafoo = cfoo : ObjectContaining<Foo>
>cafoo : ObjectContaining<{ a: number; foo: number; }>
>cfoo : ObjectContaining<Foo>

27 changes: 0 additions & 27 deletions tests/baselines/reference/recursiveTypeComparison.errors.txt

This file was deleted.

47 changes: 47 additions & 0 deletions tests/baselines/reference/specedNoStackBlown.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//// [specedNoStackBlown.ts]
// Type definitions for spected 0.7
// Project: https://github.com/25th-floor/spected
// Definitions by: Benjamin Makus <https://github.com/benneq>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.8

declare function spected<ROOTINPUT, SPEC extends SpecValue<ROOTINPUT, ROOTINPUT> = SpecValue<ROOTINPUT, ROOTINPUT>>(spec: SPEC, input: ROOTINPUT): Result<ROOTINPUT, SPEC>;

type Predicate<INPUT, ROOTINPUT> = (value: INPUT, inputs: ROOTINPUT) => boolean;

type ErrorMsg<INPUT> =
| (string | number | boolean | symbol | null | undefined | object)
| ((value: INPUT, field: string) => any);

export type Spec<INPUT, ROOTINPUT = any> = [Predicate<INPUT, ROOTINPUT>, ErrorMsg<INPUT>];

export type SpecArray<INPUT, ROOTINPUT = any> = Array<Spec<INPUT, ROOTINPUT>>;

export type SpecFunction<INPUT, ROOTINPUT = any> = [INPUT] extends [ReadonlyArray<infer U>]
? (value: INPUT) => ReadonlyArray<SpecArray<U, ROOTINPUT>>
: [INPUT] extends [object]
? (value: INPUT) => SpecObject<INPUT, ROOTINPUT>
: (value: INPUT) => SpecArray<INPUT, ROOTINPUT>;

export type SpecObject<INPUT, ROOTINPUT = any> = Partial<{[key in keyof INPUT]: SpecValue<INPUT[key], ROOTINPUT>}>;

export type SpecValue<INPUT, ROOTINPUT = any> = [INPUT] extends [ReadonlyArray<any>]
? SpecArray<INPUT, ROOTINPUT> | SpecFunction<INPUT, ROOTINPUT>
: [INPUT] extends [object]
? SpecArray<INPUT, ROOTINPUT> | SpecFunction<INPUT, ROOTINPUT> | SpecObject<INPUT, ROOTINPUT>
: SpecArray<INPUT, ROOTINPUT> | SpecFunction<INPUT, ROOTINPUT>;

export type Result<INPUT, SPEC> = {[key in keyof INPUT]: true | any[] | Result<INPUT[key], any>};

export default spected;


//// [specedNoStackBlown.js]
"use strict";
// Type definitions for spected 0.7
// Project: https://github.com/25th-floor/spected
// Definitions by: Benjamin Makus <https://github.com/benneq>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.8
exports.__esModule = true;
exports["default"] = spected;
Loading