diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index bccd6075b971a..03a9d197f462e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1364,6 +1364,7 @@ const enum IntrinsicTypeKind { Lowercase, Capitalize, Uncapitalize, + NoInfer, } const intrinsicTypeKinds: ReadonlyMap = new Map(Object.entries({ @@ -1371,6 +1372,7 @@ const intrinsicTypeKinds: ReadonlyMap = new Map(Objec Lowercase: IntrinsicTypeKind.Lowercase, Capitalize: IntrinsicTypeKind.Capitalize, Uncapitalize: IntrinsicTypeKind.Uncapitalize, + NoInfer: IntrinsicTypeKind.NoInfer, })); const SymbolLinks = class implements SymbolLinks { @@ -6728,7 +6730,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return visitAndTransformType(type, type => conditionalTypeToTypeNode(type as ConditionalType)); } if (type.flags & TypeFlags.Substitution) { - return typeToTypeNodeHelper((type as SubstitutionType).baseType, context); + const typeNode = typeToTypeNodeHelper((type as SubstitutionType).baseType, context); + const noInferSymbol = isNoInferType(type) && getGlobalTypeSymbol("NoInfer" as __String, /*reportErrors*/ false); + return noInferSymbol ? symbolToTypeNode(noInferSymbol, context, SymbolFlags.Type, [typeNode]) : typeNode; } return Debug.fail("Should be unreachable."); @@ -15887,8 +15891,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function getTypeAliasInstantiation(symbol: Symbol, typeArguments: readonly Type[] | undefined, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[]): Type { const type = getDeclaredTypeOfSymbol(symbol); - if (type === intrinsicMarkerType && intrinsicTypeKinds.has(symbol.escapedName as string) && typeArguments && typeArguments.length === 1) { - return getStringMappingType(symbol, typeArguments[0]); + if (type === intrinsicMarkerType) { + const typeKind = intrinsicTypeKinds.get(symbol.escapedName as string); + if (typeKind !== undefined && typeArguments && typeArguments.length === 1) { + return typeKind === IntrinsicTypeKind.NoInfer ? getNoInferType(typeArguments[0]) : getStringMappingType(symbol, typeArguments[0]); + } } const links = getSymbolLinks(symbol); const typeParameters = links.typeParameters!; @@ -16070,10 +16077,32 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return links.resolvedJSDocType; } + function getNoInferType(type: Type) { + return isNoInferTargetType(type) ? getOrCreateSubstitutionType(type, unknownType) : type; + } + + function isNoInferTargetType(type: Type): boolean { + // This is effectively a more conservative and predictable form of couldContainTypeVariables. We want to + // preserve NoInfer only for types that could contain type variables, but we don't want to exhaustively + // examine all object type members. + return !!(type.flags & TypeFlags.UnionOrIntersection && some((type as UnionOrIntersectionType).types, isNoInferTargetType) || + type.flags & TypeFlags.Substitution && !isNoInferType(type) && isNoInferTargetType((type as SubstitutionType).baseType) || + type.flags & TypeFlags.Object && !isEmptyAnonymousObjectType(type) || + type.flags & (TypeFlags.Instantiable & ~TypeFlags.Substitution) && !isPatternLiteralType(type)); + } + + function isNoInferType(type: Type) { + // A NoInfer type is represented as a substitution type with a TypeFlags.Unknown constraint. + return !!(type.flags & TypeFlags.Substitution && (type as SubstitutionType).constraint.flags & TypeFlags.Unknown); + } + function getSubstitutionType(baseType: Type, constraint: Type) { - if (constraint.flags & TypeFlags.AnyOrUnknown || constraint === baseType || baseType.flags & TypeFlags.Any) { - return baseType; - } + return constraint.flags & TypeFlags.AnyOrUnknown || constraint === baseType || baseType.flags & TypeFlags.Any ? + baseType : + getOrCreateSubstitutionType(baseType, constraint); + } + + function getOrCreateSubstitutionType(baseType: Type, constraint: Type) { const id = `${getTypeId(baseType)}>${getTypeId(constraint)}`; const cached = substitutionTypes.get(id); if (cached) { @@ -16087,7 +16116,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function getSubstitutionIntersection(substitutionType: SubstitutionType) { - return getIntersectionType([substitutionType.constraint, substitutionType.baseType]); + return isNoInferType(substitutionType) ? substitutionType.baseType : getIntersectionType([substitutionType.constraint, substitutionType.baseType]); } function isUnaryTupleTypeNode(node: TypeNode) { @@ -17761,7 +17790,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function getIndexType(type: Type, indexFlags = defaultIndexFlags): Type { type = getReducedType(type); - return shouldDeferIndexType(type, indexFlags) ? getIndexTypeForGenericType(type as InstantiableType | UnionOrIntersectionType, indexFlags) : + return isNoInferType(type) ? getNoInferType(getIndexType((type as SubstitutionType).baseType, indexFlags)) : + shouldDeferIndexType(type, indexFlags) ? getIndexTypeForGenericType(type as InstantiableType | UnionOrIntersectionType, indexFlags) : type.flags & TypeFlags.Union ? getIntersectionType(map((type as UnionType).types, t => getIndexType(t, indexFlags))) : type.flags & TypeFlags.Intersection ? getUnionType(map((type as IntersectionType).types, t => getIndexType(t, indexFlags))) : getObjectFlags(type) & ObjectFlags.Mapped ? getIndexTypeForMappedType(type as MappedType, indexFlags) : @@ -19849,6 +19879,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } if (flags & TypeFlags.Substitution) { const newBaseType = instantiateType((type as SubstitutionType).baseType, mapper); + if (isNoInferType(type)) { + return getNoInferType(newBaseType); + } const newConstraint = instantiateType((type as SubstitutionType).constraint, mapper); // A substitution type originates in the true branch of a conditional type and can be resolved // to just the base type in the same cases as the conditional type resolves to its true branch @@ -25348,7 +25381,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { inferFromTypes(originalSource, originalTarget); function inferFromTypes(source: Type, target: Type): void { - if (!couldContainTypeVariables(target)) { + if (!couldContainTypeVariables(target) || isNoInferType(target)) { return; } if (source === wildcardType || source === blockedStringType) { @@ -25421,6 +25454,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } if (target.flags & (TypeFlags.IndexedAccess | TypeFlags.Substitution)) { + if (isNoInferType(target)) { + return; + } target = getActualTypeVariable(target); } if (target.flags & TypeFlags.TypeVariable) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 0376471ced20b..2e3d050027bbe 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -6707,8 +6707,9 @@ export interface StringMappingType extends InstantiableType { // Substitution types are created for type parameters or indexed access types that occur in the // true branch of a conditional type. For example, in 'T extends string ? Foo : Bar', the // reference to T in Foo is resolved as a substitution type that substitutes 'string & T' for T. -// Thus, if Foo has a 'string' constraint on its type parameter, T will satisfy it. Substitution -// types disappear upon instantiation (just like type parameters). +// Thus, if Foo has a 'string' constraint on its type parameter, T will satisfy it. +// Substitution type are also created for NoInfer types. Those are represented as substitution +// types where the constraint is type 'unknown' (which is never generated for the case above). export interface SubstitutionType extends InstantiableType { objectFlags: ObjectFlags; baseType: Type; // Target type diff --git a/src/harness/fourslashInterfaceImpl.ts b/src/harness/fourslashInterfaceImpl.ts index 52e028c5070bb..309dc84fb45f4 100644 --- a/src/harness/fourslashInterfaceImpl.ts +++ b/src/harness/fourslashInterfaceImpl.ts @@ -1195,6 +1195,7 @@ export namespace Completion { typeEntry("Lowercase"), typeEntry("Capitalize"), typeEntry("Uncapitalize"), + typeEntry("NoInfer"), interfaceEntry("ThisType"), varEntry("ArrayBuffer"), interfaceEntry("ArrayBufferTypes"), diff --git a/src/lib/es5.d.ts b/src/lib/es5.d.ts index f3df23047022d..1177ed2b51395 100644 --- a/src/lib/es5.d.ts +++ b/src/lib/es5.d.ts @@ -1648,6 +1648,11 @@ type Capitalize = intrinsic; */ type Uncapitalize = intrinsic; +/** + * Marker for non-inference type position + */ +type NoInfer = intrinsic; + /** * Marker for contextual 'this' type */ diff --git a/tests/baselines/reference/noInfer.errors.txt b/tests/baselines/reference/noInfer.errors.txt new file mode 100644 index 0000000000000..2b016511a09b2 --- /dev/null +++ b/tests/baselines/reference/noInfer.errors.txt @@ -0,0 +1,125 @@ +noInfer.ts(35,13): error TS2345: Argument of type '"bar"' is not assignable to parameter of type '"foo"'. +noInfer.ts(36,14): error TS2322: Type '"bar"' is not assignable to type '"foo"'. +noInfer.ts(37,14): error TS2322: Type '"bar"' is not assignable to type '"foo"'. +noInfer.ts(38,15): error TS2322: Type '"bar"' is not assignable to type '"foo"'. +noInfer.ts(39,15): error TS2322: Type '"bar"' is not assignable to type '"foo"'. +noInfer.ts(47,30): error TS2741: Property 'woof' is missing in type 'Animal' but required in type 'Dog'. +noInfer.ts(53,16): error TS2345: Argument of type '{ x: number; }' is not assignable to parameter of type '{ x: number; y: number; }'. + Property 'y' is missing in type '{ x: number; }' but required in type '{ x: number; y: number; }'. +noInfer.ts(58,22): error TS2353: Object literal may only specify known properties, and 'y' does not exist in type '{ x: number; }'. +noInfer.ts(59,14): error TS2353: Object literal may only specify known properties, and 'y' does not exist in type '{ x: number; }'. +noInfer.ts(66,14): error TS2345: Argument of type '{}' is not assignable to parameter of type '{ foo: number; }'. + Property 'foo' is missing in type '{}' but required in type '{ foo: number; }'. + + +==== noInfer.ts (10 errors) ==== + // NoInfer is erased for primitives + + type T00 = NoInfer; + type T01 = NoInfer; + type T02 = NoInfer; + type T03 = NoInfer<"foo">; + type T04 = NoInfer<`foo${string}`>; + type T05 = NoInfer<`foo${string}` & `${string}bar`>; + type T06 = NoInfer<{}>; + + // NoInfer is preserved for object types + + type T10 = NoInfer; + type T11 = NoInfer<{ x: string }>; + + // NoInfer is erased if it has no effect + + type T20 = NoInfer>; + type T21 = NoInfer & string>; + type T22 = NoInfer & string[]>; + + // keyof NoInfer is transformed into NoInfer + + type T30 = keyof NoInfer<{ a: string, b: string }>; + type T31 = keyof NoInfer; + type T32 = { [K in keyof NoInfer<{ a: string, b: string }>]: K }; + + declare function foo1(a: T, b: NoInfer): void + declare function foo2(a: T, b: NoInfer[]): void + declare function foo3(a: T, b: NoInfer): void + declare function foo4(a: T, b: { x: NoInfer }): void + declare function foo5(a: T, b: NoInfer<{ x: T }>): void + + foo1('foo', 'foo') // ok + foo1('foo', 'bar') // error + ~~~~~ +!!! error TS2345: Argument of type '"bar"' is not assignable to parameter of type '"foo"'. + foo2('foo', ['bar']) // error + ~~~~~ +!!! error TS2322: Type '"bar"' is not assignable to type '"foo"'. + foo3('foo', ['bar']) // error + ~~~~~ +!!! error TS2322: Type '"bar"' is not assignable to type '"foo"'. + foo4('foo', { x: 'bar' }) // error + ~ +!!! error TS2322: Type '"bar"' is not assignable to type '"foo"'. +!!! related TS6500 noInfer.ts:31:52: The expected type comes from property 'x' which is declared here on type '{ x: "foo"; }' + foo5('foo', { x: 'bar' }) // error + ~ +!!! error TS2322: Type '"bar"' is not assignable to type '"foo"'. +!!! related TS6500 noInfer.ts:32:60: The expected type comes from property 'x' which is declared here on type 'NoInfer<{ x: "foo"; }>' + + declare class Animal { move(): void } + declare class Dog extends Animal { woof(): void } + declare function doSomething(value: T, getDefault: () => NoInfer): void; + + doSomething(new Animal(), () => new Animal()); // ok + doSomething(new Animal(), () => new Dog()); // ok + doSomething(new Dog(), () => new Animal()); // error + ~~~~~~~~~~~~ +!!! error TS2741: Property 'woof' is missing in type 'Animal' but required in type 'Dog'. +!!! related TS2728 noInfer.ts:42:36: 'woof' is declared here. +!!! related TS6502 noInfer.ts:43:55: The expected type comes from the return type of this signature. + + declare function assertEqual(actual: T, expected: NoInfer): boolean; + + assertEqual({ x: 1 }, { x: 3 }); // ok + const g = { x: 3, y: 2 }; + assertEqual(g, { x: 3 }); // error + ~~~~~~~~ +!!! error TS2345: Argument of type '{ x: number; }' is not assignable to parameter of type '{ x: number; y: number; }'. +!!! error TS2345: Property 'y' is missing in type '{ x: number; }' but required in type '{ x: number; y: number; }'. +!!! related TS2728 noInfer.ts:52:19: 'y' is declared here. + + declare function invoke(func: (value: T) => R, value: NoInfer): R; + declare function test(value: { x: number; }): number; + + invoke(test, { x: 1, y: 2 }); // error + ~ +!!! error TS2353: Object literal may only specify known properties, and 'y' does not exist in type '{ x: number; }'. + test({ x: 1, y: 2 }); // error + ~ +!!! error TS2353: Object literal may only specify known properties, and 'y' does not exist in type '{ x: number; }'. + + type Component = { props: Props; }; + declare function doWork(Component: Component, props: NoInfer): void; + declare const comp: Component<{ foo: number }>; + + doWork(comp, { foo: 42 }); // ok + doWork(comp, {}); // error + ~~ +!!! error TS2345: Argument of type '{}' is not assignable to parameter of type '{ foo: number; }'. +!!! error TS2345: Property 'foo' is missing in type '{}' but required in type '{ foo: number; }'. +!!! related TS2728 noInfer.ts:63:33: 'foo' is declared here. + + declare function mutate(callback: (a: NoInfer, b: number) => T): T; + const mutate1 = mutate((a, b) => b); + + declare class ExampleClass {} + class OkClass { + constructor(private clazz: ExampleClass, private _value: NoInfer) {} + + get value(): T { + return this._value; // ok + } + } + class OkClass2 { + constructor(private clazz: ExampleClass, public _value: NoInfer) {} + } + \ No newline at end of file diff --git a/tests/baselines/reference/noInfer.js b/tests/baselines/reference/noInfer.js new file mode 100644 index 0000000000000..59e27f3755efd --- /dev/null +++ b/tests/baselines/reference/noInfer.js @@ -0,0 +1,202 @@ +//// [tests/cases/conformance/types/typeRelationships/typeInference/noInfer.ts] //// + +//// [noInfer.ts] +// NoInfer is erased for primitives + +type T00 = NoInfer; +type T01 = NoInfer; +type T02 = NoInfer; +type T03 = NoInfer<"foo">; +type T04 = NoInfer<`foo${string}`>; +type T05 = NoInfer<`foo${string}` & `${string}bar`>; +type T06 = NoInfer<{}>; + +// NoInfer is preserved for object types + +type T10 = NoInfer; +type T11 = NoInfer<{ x: string }>; + +// NoInfer is erased if it has no effect + +type T20 = NoInfer>; +type T21 = NoInfer & string>; +type T22 = NoInfer & string[]>; + +// keyof NoInfer is transformed into NoInfer + +type T30 = keyof NoInfer<{ a: string, b: string }>; +type T31 = keyof NoInfer; +type T32 = { [K in keyof NoInfer<{ a: string, b: string }>]: K }; + +declare function foo1(a: T, b: NoInfer): void +declare function foo2(a: T, b: NoInfer[]): void +declare function foo3(a: T, b: NoInfer): void +declare function foo4(a: T, b: { x: NoInfer }): void +declare function foo5(a: T, b: NoInfer<{ x: T }>): void + +foo1('foo', 'foo') // ok +foo1('foo', 'bar') // error +foo2('foo', ['bar']) // error +foo3('foo', ['bar']) // error +foo4('foo', { x: 'bar' }) // error +foo5('foo', { x: 'bar' }) // error + +declare class Animal { move(): void } +declare class Dog extends Animal { woof(): void } +declare function doSomething(value: T, getDefault: () => NoInfer): void; + +doSomething(new Animal(), () => new Animal()); // ok +doSomething(new Animal(), () => new Dog()); // ok +doSomething(new Dog(), () => new Animal()); // error + +declare function assertEqual(actual: T, expected: NoInfer): boolean; + +assertEqual({ x: 1 }, { x: 3 }); // ok +const g = { x: 3, y: 2 }; +assertEqual(g, { x: 3 }); // error + +declare function invoke(func: (value: T) => R, value: NoInfer): R; +declare function test(value: { x: number; }): number; + +invoke(test, { x: 1, y: 2 }); // error +test({ x: 1, y: 2 }); // error + +type Component = { props: Props; }; +declare function doWork(Component: Component, props: NoInfer): void; +declare const comp: Component<{ foo: number }>; + +doWork(comp, { foo: 42 }); // ok +doWork(comp, {}); // error + +declare function mutate(callback: (a: NoInfer, b: number) => T): T; +const mutate1 = mutate((a, b) => b); + +declare class ExampleClass {} +class OkClass { + constructor(private clazz: ExampleClass, private _value: NoInfer) {} + + get value(): T { + return this._value; // ok + } +} +class OkClass2 { + constructor(private clazz: ExampleClass, public _value: NoInfer) {} +} + + +//// [noInfer.js] +"use strict"; +// NoInfer is erased for primitives +foo1('foo', 'foo'); // ok +foo1('foo', 'bar'); // error +foo2('foo', ['bar']); // error +foo3('foo', ['bar']); // error +foo4('foo', { x: 'bar' }); // error +foo5('foo', { x: 'bar' }); // error +doSomething(new Animal(), function () { return new Animal(); }); // ok +doSomething(new Animal(), function () { return new Dog(); }); // ok +doSomething(new Dog(), function () { return new Animal(); }); // error +assertEqual({ x: 1 }, { x: 3 }); // ok +var g = { x: 3, y: 2 }; +assertEqual(g, { x: 3 }); // error +invoke(test, { x: 1, y: 2 }); // error +test({ x: 1, y: 2 }); // error +doWork(comp, { foo: 42 }); // ok +doWork(comp, {}); // error +var mutate1 = mutate(function (a, b) { return b; }); +var OkClass = /** @class */ (function () { + function OkClass(clazz, _value) { + this.clazz = clazz; + this._value = _value; + } + Object.defineProperty(OkClass.prototype, "value", { + get: function () { + return this._value; // ok + }, + enumerable: false, + configurable: true + }); + return OkClass; +}()); +var OkClass2 = /** @class */ (function () { + function OkClass2(clazz, _value) { + this.clazz = clazz; + this._value = _value; + } + return OkClass2; +}()); + + +//// [noInfer.d.ts] +type T00 = NoInfer; +type T01 = NoInfer; +type T02 = NoInfer; +type T03 = NoInfer<"foo">; +type T04 = NoInfer<`foo${string}`>; +type T05 = NoInfer<`foo${string}` & `${string}bar`>; +type T06 = NoInfer<{}>; +type T10 = NoInfer; +type T11 = NoInfer<{ + x: string; +}>; +type T20 = NoInfer>; +type T21 = NoInfer & string>; +type T22 = NoInfer & string[]>; +type T30 = keyof NoInfer<{ + a: string; + b: string; +}>; +type T31 = keyof NoInfer; +type T32 = { + [K in keyof NoInfer<{ + a: string; + b: string; + }>]: K; +}; +declare function foo1(a: T, b: NoInfer): void; +declare function foo2(a: T, b: NoInfer[]): void; +declare function foo3(a: T, b: NoInfer): void; +declare function foo4(a: T, b: { + x: NoInfer; +}): void; +declare function foo5(a: T, b: NoInfer<{ + x: T; +}>): void; +declare class Animal { + move(): void; +} +declare class Dog extends Animal { + woof(): void; +} +declare function doSomething(value: T, getDefault: () => NoInfer): void; +declare function assertEqual(actual: T, expected: NoInfer): boolean; +declare const g: { + x: number; + y: number; +}; +declare function invoke(func: (value: T) => R, value: NoInfer): R; +declare function test(value: { + x: number; +}): number; +type Component = { + props: Props; +}; +declare function doWork(Component: Component, props: NoInfer): void; +declare const comp: Component<{ + foo: number; +}>; +declare function mutate(callback: (a: NoInfer, b: number) => T): T; +declare const mutate1: unknown; +declare class ExampleClass { +} +declare class OkClass { + private clazz; + private _value; + constructor(clazz: ExampleClass, _value: NoInfer); + get value(): T; +} +declare class OkClass2 { + private clazz; + _value: NoInfer; + constructor(clazz: ExampleClass, _value: NoInfer); +} diff --git a/tests/baselines/reference/noInfer.symbols b/tests/baselines/reference/noInfer.symbols new file mode 100644 index 0000000000000..7a36c6fccb9a1 --- /dev/null +++ b/tests/baselines/reference/noInfer.symbols @@ -0,0 +1,329 @@ +//// [tests/cases/conformance/types/typeRelationships/typeInference/noInfer.ts] //// + +=== noInfer.ts === +// NoInfer is erased for primitives + +type T00 = NoInfer; +>T00 : Symbol(T00, Decl(noInfer.ts, 0, 0)) +>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --)) + +type T01 = NoInfer; +>T01 : Symbol(T01, Decl(noInfer.ts, 2, 27)) +>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --)) + +type T02 = NoInfer; +>T02 : Symbol(T02, Decl(noInfer.ts, 3, 46)) +>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --)) + +type T03 = NoInfer<"foo">; +>T03 : Symbol(T03, Decl(noInfer.ts, 4, 30)) +>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --)) + +type T04 = NoInfer<`foo${string}`>; +>T04 : Symbol(T04, Decl(noInfer.ts, 5, 26)) +>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --)) + +type T05 = NoInfer<`foo${string}` & `${string}bar`>; +>T05 : Symbol(T05, Decl(noInfer.ts, 6, 35)) +>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --)) + +type T06 = NoInfer<{}>; +>T06 : Symbol(T06, Decl(noInfer.ts, 7, 52)) +>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --)) + +// NoInfer is preserved for object types + +type T10 = NoInfer; +>T10 : Symbol(T10, Decl(noInfer.ts, 8, 23)) +>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --)) + +type T11 = NoInfer<{ x: string }>; +>T11 : Symbol(T11, Decl(noInfer.ts, 12, 29)) +>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --)) +>x : Symbol(x, Decl(noInfer.ts, 13, 20)) + +// NoInfer is erased if it has no effect + +type T20 = NoInfer>; +>T20 : Symbol(T20, Decl(noInfer.ts, 13, 34)) +>T : Symbol(T, Decl(noInfer.ts, 17, 9)) +>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --)) +>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(noInfer.ts, 17, 9)) + +type T21 = NoInfer & string>; +>T21 : Symbol(T21, Decl(noInfer.ts, 17, 34)) +>T : Symbol(T, Decl(noInfer.ts, 18, 9)) +>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --)) +>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(noInfer.ts, 18, 9)) + +type T22 = NoInfer & string[]>; +>T22 : Symbol(T22, Decl(noInfer.ts, 18, 43)) +>T : Symbol(T, Decl(noInfer.ts, 19, 9)) +>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --)) +>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(noInfer.ts, 19, 9)) + +// keyof NoInfer is transformed into NoInfer + +type T30 = keyof NoInfer<{ a: string, b: string }>; +>T30 : Symbol(T30, Decl(noInfer.ts, 19, 45)) +>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --)) +>a : Symbol(a, Decl(noInfer.ts, 23, 26)) +>b : Symbol(b, Decl(noInfer.ts, 23, 37)) + +type T31 = keyof NoInfer; +>T31 : Symbol(T31, Decl(noInfer.ts, 23, 51)) +>T : Symbol(T, Decl(noInfer.ts, 24, 9)) +>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(noInfer.ts, 24, 9)) + +type T32 = { [K in keyof NoInfer<{ a: string, b: string }>]: K }; +>T32 : Symbol(T32, Decl(noInfer.ts, 24, 31)) +>K : Symbol(K, Decl(noInfer.ts, 25, 14)) +>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --)) +>a : Symbol(a, Decl(noInfer.ts, 25, 34)) +>b : Symbol(b, Decl(noInfer.ts, 25, 45)) +>K : Symbol(K, Decl(noInfer.ts, 25, 14)) + +declare function foo1(a: T, b: NoInfer): void +>foo1 : Symbol(foo1, Decl(noInfer.ts, 25, 65)) +>T : Symbol(T, Decl(noInfer.ts, 27, 22)) +>a : Symbol(a, Decl(noInfer.ts, 27, 40)) +>T : Symbol(T, Decl(noInfer.ts, 27, 22)) +>b : Symbol(b, Decl(noInfer.ts, 27, 45)) +>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(noInfer.ts, 27, 22)) + +declare function foo2(a: T, b: NoInfer[]): void +>foo2 : Symbol(foo2, Decl(noInfer.ts, 27, 66)) +>T : Symbol(T, Decl(noInfer.ts, 28, 22)) +>a : Symbol(a, Decl(noInfer.ts, 28, 40)) +>T : Symbol(T, Decl(noInfer.ts, 28, 22)) +>b : Symbol(b, Decl(noInfer.ts, 28, 45)) +>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(noInfer.ts, 28, 22)) + +declare function foo3(a: T, b: NoInfer): void +>foo3 : Symbol(foo3, Decl(noInfer.ts, 28, 68)) +>T : Symbol(T, Decl(noInfer.ts, 29, 22)) +>a : Symbol(a, Decl(noInfer.ts, 29, 40)) +>T : Symbol(T, Decl(noInfer.ts, 29, 22)) +>b : Symbol(b, Decl(noInfer.ts, 29, 45)) +>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(noInfer.ts, 29, 22)) + +declare function foo4(a: T, b: { x: NoInfer }): void +>foo4 : Symbol(foo4, Decl(noInfer.ts, 29, 68)) +>T : Symbol(T, Decl(noInfer.ts, 30, 22)) +>a : Symbol(a, Decl(noInfer.ts, 30, 40)) +>T : Symbol(T, Decl(noInfer.ts, 30, 22)) +>b : Symbol(b, Decl(noInfer.ts, 30, 45)) +>x : Symbol(x, Decl(noInfer.ts, 30, 50)) +>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(noInfer.ts, 30, 22)) + +declare function foo5(a: T, b: NoInfer<{ x: T }>): void +>foo5 : Symbol(foo5, Decl(noInfer.ts, 30, 73)) +>T : Symbol(T, Decl(noInfer.ts, 31, 22)) +>a : Symbol(a, Decl(noInfer.ts, 31, 40)) +>T : Symbol(T, Decl(noInfer.ts, 31, 22)) +>b : Symbol(b, Decl(noInfer.ts, 31, 45)) +>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --)) +>x : Symbol(x, Decl(noInfer.ts, 31, 58)) +>T : Symbol(T, Decl(noInfer.ts, 31, 22)) + +foo1('foo', 'foo') // ok +>foo1 : Symbol(foo1, Decl(noInfer.ts, 25, 65)) + +foo1('foo', 'bar') // error +>foo1 : Symbol(foo1, Decl(noInfer.ts, 25, 65)) + +foo2('foo', ['bar']) // error +>foo2 : Symbol(foo2, Decl(noInfer.ts, 27, 66)) + +foo3('foo', ['bar']) // error +>foo3 : Symbol(foo3, Decl(noInfer.ts, 28, 68)) + +foo4('foo', { x: 'bar' }) // error +>foo4 : Symbol(foo4, Decl(noInfer.ts, 29, 68)) +>x : Symbol(x, Decl(noInfer.ts, 37, 13)) + +foo5('foo', { x: 'bar' }) // error +>foo5 : Symbol(foo5, Decl(noInfer.ts, 30, 73)) +>x : Symbol(x, Decl(noInfer.ts, 38, 13)) + +declare class Animal { move(): void } +>Animal : Symbol(Animal, Decl(noInfer.ts, 38, 25)) +>move : Symbol(Animal.move, Decl(noInfer.ts, 40, 22)) + +declare class Dog extends Animal { woof(): void } +>Dog : Symbol(Dog, Decl(noInfer.ts, 40, 37)) +>Animal : Symbol(Animal, Decl(noInfer.ts, 38, 25)) +>woof : Symbol(Dog.woof, Decl(noInfer.ts, 41, 34)) + +declare function doSomething(value: T, getDefault: () => NoInfer): void; +>doSomething : Symbol(doSomething, Decl(noInfer.ts, 41, 49)) +>T : Symbol(T, Decl(noInfer.ts, 42, 29)) +>value : Symbol(value, Decl(noInfer.ts, 42, 32)) +>T : Symbol(T, Decl(noInfer.ts, 42, 29)) +>getDefault : Symbol(getDefault, Decl(noInfer.ts, 42, 41)) +>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(noInfer.ts, 42, 29)) + +doSomething(new Animal(), () => new Animal()); // ok +>doSomething : Symbol(doSomething, Decl(noInfer.ts, 41, 49)) +>Animal : Symbol(Animal, Decl(noInfer.ts, 38, 25)) +>Animal : Symbol(Animal, Decl(noInfer.ts, 38, 25)) + +doSomething(new Animal(), () => new Dog()); // ok +>doSomething : Symbol(doSomething, Decl(noInfer.ts, 41, 49)) +>Animal : Symbol(Animal, Decl(noInfer.ts, 38, 25)) +>Dog : Symbol(Dog, Decl(noInfer.ts, 40, 37)) + +doSomething(new Dog(), () => new Animal()); // error +>doSomething : Symbol(doSomething, Decl(noInfer.ts, 41, 49)) +>Dog : Symbol(Dog, Decl(noInfer.ts, 40, 37)) +>Animal : Symbol(Animal, Decl(noInfer.ts, 38, 25)) + +declare function assertEqual(actual: T, expected: NoInfer): boolean; +>assertEqual : Symbol(assertEqual, Decl(noInfer.ts, 46, 43)) +>T : Symbol(T, Decl(noInfer.ts, 48, 29)) +>actual : Symbol(actual, Decl(noInfer.ts, 48, 32)) +>T : Symbol(T, Decl(noInfer.ts, 48, 29)) +>expected : Symbol(expected, Decl(noInfer.ts, 48, 42)) +>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(noInfer.ts, 48, 29)) + +assertEqual({ x: 1 }, { x: 3 }); // ok +>assertEqual : Symbol(assertEqual, Decl(noInfer.ts, 46, 43)) +>x : Symbol(x, Decl(noInfer.ts, 50, 13)) +>x : Symbol(x, Decl(noInfer.ts, 50, 23)) + +const g = { x: 3, y: 2 }; +>g : Symbol(g, Decl(noInfer.ts, 51, 5)) +>x : Symbol(x, Decl(noInfer.ts, 51, 11)) +>y : Symbol(y, Decl(noInfer.ts, 51, 17)) + +assertEqual(g, { x: 3 }); // error +>assertEqual : Symbol(assertEqual, Decl(noInfer.ts, 46, 43)) +>g : Symbol(g, Decl(noInfer.ts, 51, 5)) +>x : Symbol(x, Decl(noInfer.ts, 52, 16)) + +declare function invoke(func: (value: T) => R, value: NoInfer): R; +>invoke : Symbol(invoke, Decl(noInfer.ts, 52, 25)) +>T : Symbol(T, Decl(noInfer.ts, 54, 24)) +>R : Symbol(R, Decl(noInfer.ts, 54, 26)) +>func : Symbol(func, Decl(noInfer.ts, 54, 30)) +>value : Symbol(value, Decl(noInfer.ts, 54, 37)) +>T : Symbol(T, Decl(noInfer.ts, 54, 24)) +>R : Symbol(R, Decl(noInfer.ts, 54, 26)) +>value : Symbol(value, Decl(noInfer.ts, 54, 52)) +>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(noInfer.ts, 54, 24)) +>R : Symbol(R, Decl(noInfer.ts, 54, 26)) + +declare function test(value: { x: number; }): number; +>test : Symbol(test, Decl(noInfer.ts, 54, 75)) +>value : Symbol(value, Decl(noInfer.ts, 55, 22)) +>x : Symbol(x, Decl(noInfer.ts, 55, 30)) + +invoke(test, { x: 1, y: 2 }); // error +>invoke : Symbol(invoke, Decl(noInfer.ts, 52, 25)) +>test : Symbol(test, Decl(noInfer.ts, 54, 75)) +>x : Symbol(x, Decl(noInfer.ts, 57, 14)) +>y : Symbol(y, Decl(noInfer.ts, 57, 20)) + +test({ x: 1, y: 2 }); // error +>test : Symbol(test, Decl(noInfer.ts, 54, 75)) +>x : Symbol(x, Decl(noInfer.ts, 58, 6)) +>y : Symbol(y, Decl(noInfer.ts, 58, 12)) + +type Component = { props: Props; }; +>Component : Symbol(Component, Decl(noInfer.ts, 58, 21)) +>Props : Symbol(Props, Decl(noInfer.ts, 60, 15)) +>props : Symbol(props, Decl(noInfer.ts, 60, 25)) +>Props : Symbol(Props, Decl(noInfer.ts, 60, 15)) + +declare function doWork(Component: Component, props: NoInfer): void; +>doWork : Symbol(doWork, Decl(noInfer.ts, 60, 42)) +>Props : Symbol(Props, Decl(noInfer.ts, 61, 24)) +>Component : Symbol(Component, Decl(noInfer.ts, 61, 31)) +>Component : Symbol(Component, Decl(noInfer.ts, 58, 21)) +>Props : Symbol(Props, Decl(noInfer.ts, 61, 24)) +>props : Symbol(props, Decl(noInfer.ts, 61, 59)) +>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --)) +>Props : Symbol(Props, Decl(noInfer.ts, 61, 24)) + +declare const comp: Component<{ foo: number }>; +>comp : Symbol(comp, Decl(noInfer.ts, 62, 13)) +>Component : Symbol(Component, Decl(noInfer.ts, 58, 21)) +>foo : Symbol(foo, Decl(noInfer.ts, 62, 31)) + +doWork(comp, { foo: 42 }); // ok +>doWork : Symbol(doWork, Decl(noInfer.ts, 60, 42)) +>comp : Symbol(comp, Decl(noInfer.ts, 62, 13)) +>foo : Symbol(foo, Decl(noInfer.ts, 64, 14)) + +doWork(comp, {}); // error +>doWork : Symbol(doWork, Decl(noInfer.ts, 60, 42)) +>comp : Symbol(comp, Decl(noInfer.ts, 62, 13)) + +declare function mutate(callback: (a: NoInfer, b: number) => T): T; +>mutate : Symbol(mutate, Decl(noInfer.ts, 65, 17)) +>T : Symbol(T, Decl(noInfer.ts, 67, 24)) +>callback : Symbol(callback, Decl(noInfer.ts, 67, 27)) +>a : Symbol(a, Decl(noInfer.ts, 67, 38)) +>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(noInfer.ts, 67, 24)) +>b : Symbol(b, Decl(noInfer.ts, 67, 52)) +>T : Symbol(T, Decl(noInfer.ts, 67, 24)) +>T : Symbol(T, Decl(noInfer.ts, 67, 24)) + +const mutate1 = mutate((a, b) => b); +>mutate1 : Symbol(mutate1, Decl(noInfer.ts, 68, 5)) +>mutate : Symbol(mutate, Decl(noInfer.ts, 65, 17)) +>a : Symbol(a, Decl(noInfer.ts, 68, 24)) +>b : Symbol(b, Decl(noInfer.ts, 68, 26)) +>b : Symbol(b, Decl(noInfer.ts, 68, 26)) + +declare class ExampleClass {} +>ExampleClass : Symbol(ExampleClass, Decl(noInfer.ts, 68, 36)) +>T : Symbol(T, Decl(noInfer.ts, 70, 27)) + +class OkClass { +>OkClass : Symbol(OkClass, Decl(noInfer.ts, 70, 32)) +>T : Symbol(T, Decl(noInfer.ts, 71, 14)) + + constructor(private clazz: ExampleClass, private _value: NoInfer) {} +>clazz : Symbol(OkClass.clazz, Decl(noInfer.ts, 72, 16)) +>ExampleClass : Symbol(ExampleClass, Decl(noInfer.ts, 68, 36)) +>T : Symbol(T, Decl(noInfer.ts, 71, 14)) +>_value : Symbol(OkClass._value, Decl(noInfer.ts, 72, 47)) +>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(noInfer.ts, 71, 14)) + + get value(): T { +>value : Symbol(OkClass.value, Decl(noInfer.ts, 72, 78)) +>T : Symbol(T, Decl(noInfer.ts, 71, 14)) + + return this._value; // ok +>this._value : Symbol(OkClass._value, Decl(noInfer.ts, 72, 47)) +>this : Symbol(OkClass, Decl(noInfer.ts, 70, 32)) +>_value : Symbol(OkClass._value, Decl(noInfer.ts, 72, 47)) + } +} +class OkClass2 { +>OkClass2 : Symbol(OkClass2, Decl(noInfer.ts, 77, 1)) +>T : Symbol(T, Decl(noInfer.ts, 78, 15)) + + constructor(private clazz: ExampleClass, public _value: NoInfer) {} +>clazz : Symbol(OkClass2.clazz, Decl(noInfer.ts, 79, 16)) +>ExampleClass : Symbol(ExampleClass, Decl(noInfer.ts, 68, 36)) +>T : Symbol(T, Decl(noInfer.ts, 78, 15)) +>_value : Symbol(OkClass2._value, Decl(noInfer.ts, 79, 47)) +>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(noInfer.ts, 78, 15)) +} + diff --git a/tests/baselines/reference/noInfer.types b/tests/baselines/reference/noInfer.types new file mode 100644 index 0000000000000..0b747c064a450 --- /dev/null +++ b/tests/baselines/reference/noInfer.types @@ -0,0 +1,301 @@ +//// [tests/cases/conformance/types/typeRelationships/typeInference/noInfer.ts] //// + +=== noInfer.ts === +// NoInfer is erased for primitives + +type T00 = NoInfer; +>T00 : string + +type T01 = NoInfer; +>T01 : string | number | boolean + +type T02 = NoInfer; +>T02 : undefined + +type T03 = NoInfer<"foo">; +>T03 : "foo" + +type T04 = NoInfer<`foo${string}`>; +>T04 : `foo${string}` + +type T05 = NoInfer<`foo${string}` & `${string}bar`>; +>T05 : `foo${string}` & `${string}bar` + +type T06 = NoInfer<{}>; +>T06 : {} + +// NoInfer is preserved for object types + +type T10 = NoInfer; +>T10 : NoInfer + +type T11 = NoInfer<{ x: string }>; +>T11 : NoInfer<{ x: string; }> +>x : string + +// NoInfer is erased if it has no effect + +type T20 = NoInfer>; +>T20 : NoInfer + +type T21 = NoInfer & string>; +>T21 : NoInfer & string + +type T22 = NoInfer & string[]>; +>T22 : NoInfer & string[]> + +// keyof NoInfer is transformed into NoInfer + +type T30 = keyof NoInfer<{ a: string, b: string }>; +>T30 : "a" | "b" +>a : string +>b : string + +type T31 = keyof NoInfer; +>T31 : NoInfer + +type T32 = { [K in keyof NoInfer<{ a: string, b: string }>]: K }; +>T32 : { a: "a"; b: "b"; } +>a : string +>b : string + +declare function foo1(a: T, b: NoInfer): void +>foo1 : (a: T, b: NoInfer) => void +>a : T +>b : NoInfer + +declare function foo2(a: T, b: NoInfer[]): void +>foo2 : (a: T, b: NoInfer[]) => void +>a : T +>b : NoInfer[] + +declare function foo3(a: T, b: NoInfer): void +>foo3 : (a: T, b: NoInfer) => void +>a : T +>b : NoInfer + +declare function foo4(a: T, b: { x: NoInfer }): void +>foo4 : (a: T, b: { x: NoInfer;}) => void +>a : T +>b : { x: NoInfer; } +>x : NoInfer + +declare function foo5(a: T, b: NoInfer<{ x: T }>): void +>foo5 : (a: T, b: NoInfer<{ x: T;}>) => void +>a : T +>b : NoInfer<{ x: T; }> +>x : T + +foo1('foo', 'foo') // ok +>foo1('foo', 'foo') : void +>foo1 : (a: T, b: NoInfer) => void +>'foo' : "foo" +>'foo' : "foo" + +foo1('foo', 'bar') // error +>foo1('foo', 'bar') : void +>foo1 : (a: T, b: NoInfer) => void +>'foo' : "foo" +>'bar' : "bar" + +foo2('foo', ['bar']) // error +>foo2('foo', ['bar']) : void +>foo2 : (a: T, b: NoInfer[]) => void +>'foo' : "foo" +>['bar'] : "bar"[] +>'bar' : "bar" + +foo3('foo', ['bar']) // error +>foo3('foo', ['bar']) : void +>foo3 : (a: T, b: NoInfer) => void +>'foo' : "foo" +>['bar'] : "bar"[] +>'bar' : "bar" + +foo4('foo', { x: 'bar' }) // error +>foo4('foo', { x: 'bar' }) : void +>foo4 : (a: T, b: { x: NoInfer; }) => void +>'foo' : "foo" +>{ x: 'bar' } : { x: "bar"; } +>x : "bar" +>'bar' : "bar" + +foo5('foo', { x: 'bar' }) // error +>foo5('foo', { x: 'bar' }) : void +>foo5 : (a: T, b: NoInfer<{ x: T; }>) => void +>'foo' : "foo" +>{ x: 'bar' } : { x: "bar"; } +>x : "bar" +>'bar' : "bar" + +declare class Animal { move(): void } +>Animal : Animal +>move : () => void + +declare class Dog extends Animal { woof(): void } +>Dog : Dog +>Animal : Animal +>woof : () => void + +declare function doSomething(value: T, getDefault: () => NoInfer): void; +>doSomething : (value: T, getDefault: () => NoInfer) => void +>value : T +>getDefault : () => NoInfer + +doSomething(new Animal(), () => new Animal()); // ok +>doSomething(new Animal(), () => new Animal()) : void +>doSomething : (value: T, getDefault: () => NoInfer) => void +>new Animal() : Animal +>Animal : typeof Animal +>() => new Animal() : () => Animal +>new Animal() : Animal +>Animal : typeof Animal + +doSomething(new Animal(), () => new Dog()); // ok +>doSomething(new Animal(), () => new Dog()) : void +>doSomething : (value: T, getDefault: () => NoInfer) => void +>new Animal() : Animal +>Animal : typeof Animal +>() => new Dog() : () => Dog +>new Dog() : Dog +>Dog : typeof Dog + +doSomething(new Dog(), () => new Animal()); // error +>doSomething(new Dog(), () => new Animal()) : void +>doSomething : (value: T, getDefault: () => NoInfer) => void +>new Dog() : Dog +>Dog : typeof Dog +>() => new Animal() : () => Animal +>new Animal() : Animal +>Animal : typeof Animal + +declare function assertEqual(actual: T, expected: NoInfer): boolean; +>assertEqual : (actual: T, expected: NoInfer) => boolean +>actual : T +>expected : NoInfer + +assertEqual({ x: 1 }, { x: 3 }); // ok +>assertEqual({ x: 1 }, { x: 3 }) : boolean +>assertEqual : (actual: T, expected: NoInfer) => boolean +>{ x: 1 } : { x: number; } +>x : number +>1 : 1 +>{ x: 3 } : { x: number; } +>x : number +>3 : 3 + +const g = { x: 3, y: 2 }; +>g : { x: number; y: number; } +>{ x: 3, y: 2 } : { x: number; y: number; } +>x : number +>3 : 3 +>y : number +>2 : 2 + +assertEqual(g, { x: 3 }); // error +>assertEqual(g, { x: 3 }) : boolean +>assertEqual : (actual: T, expected: NoInfer) => boolean +>g : { x: number; y: number; } +>{ x: 3 } : { x: number; } +>x : number +>3 : 3 + +declare function invoke(func: (value: T) => R, value: NoInfer): R; +>invoke : (func: (value: T) => R, value: NoInfer) => R +>func : (value: T) => R +>value : T +>value : NoInfer + +declare function test(value: { x: number; }): number; +>test : (value: { x: number;}) => number +>value : { x: number; } +>x : number + +invoke(test, { x: 1, y: 2 }); // error +>invoke(test, { x: 1, y: 2 }) : number +>invoke : (func: (value: T) => R, value: NoInfer) => R +>test : (value: { x: number; }) => number +>{ x: 1, y: 2 } : { x: number; y: number; } +>x : number +>1 : 1 +>y : number +>2 : 2 + +test({ x: 1, y: 2 }); // error +>test({ x: 1, y: 2 }) : number +>test : (value: { x: number; }) => number +>{ x: 1, y: 2 } : { x: number; y: number; } +>x : number +>1 : 1 +>y : number +>2 : 2 + +type Component = { props: Props; }; +>Component : Component +>props : Props + +declare function doWork(Component: Component, props: NoInfer): void; +>doWork : (Component: Component, props: NoInfer) => void +>Component : Component +>props : NoInfer + +declare const comp: Component<{ foo: number }>; +>comp : Component<{ foo: number; }> +>foo : number + +doWork(comp, { foo: 42 }); // ok +>doWork(comp, { foo: 42 }) : void +>doWork : (Component: Component, props: NoInfer) => void +>comp : Component<{ foo: number; }> +>{ foo: 42 } : { foo: number; } +>foo : number +>42 : 42 + +doWork(comp, {}); // error +>doWork(comp, {}) : void +>doWork : (Component: Component, props: NoInfer) => void +>comp : Component<{ foo: number; }> +>{} : {} + +declare function mutate(callback: (a: NoInfer, b: number) => T): T; +>mutate : (callback: (a: NoInfer, b: number) => T) => T +>callback : (a: NoInfer, b: number) => T +>a : NoInfer +>b : number + +const mutate1 = mutate((a, b) => b); +>mutate1 : unknown +>mutate((a, b) => b) : unknown +>mutate : (callback: (a: NoInfer, b: number) => T) => T +>(a, b) => b : (a: unknown, b: number) => number +>a : unknown +>b : number +>b : number + +declare class ExampleClass {} +>ExampleClass : ExampleClass + +class OkClass { +>OkClass : OkClass + + constructor(private clazz: ExampleClass, private _value: NoInfer) {} +>clazz : ExampleClass +>_value : NoInfer + + get value(): T { +>value : T + + return this._value; // ok +>this._value : NoInfer +>this : this +>_value : NoInfer + } +} +class OkClass2 { +>OkClass2 : OkClass2 + + constructor(private clazz: ExampleClass, public _value: NoInfer) {} +>clazz : ExampleClass +>_value : NoInfer +} + diff --git a/tests/baselines/reference/noInferRedeclaration.js b/tests/baselines/reference/noInferRedeclaration.js new file mode 100644 index 0000000000000..a9afd9107f829 --- /dev/null +++ b/tests/baselines/reference/noInferRedeclaration.js @@ -0,0 +1,24 @@ +//// [tests/cases/conformance/types/typeRelationships/typeInference/noInferRedeclaration.ts] //// + +//// [a.ts] +export const f = (x: T, y: NoInfer) => x; + +//// [b.ts] +import { f } from "./a"; + +type NoInfer = T & number; + +export const g = f; + + +//// [a.js] +export const f = (x, y) => x; +//// [b.js] +import { f } from "./a"; +export const g = f; + + +//// [a.d.ts] +export declare const f: (x: T, y: NoInfer) => T; +//// [b.d.ts] +export declare const g: (x: T, y: globalThis.NoInfer) => T; diff --git a/tests/baselines/reference/noInferRedeclaration.symbols b/tests/baselines/reference/noInferRedeclaration.symbols new file mode 100644 index 0000000000000..b7b3bb5529c86 --- /dev/null +++ b/tests/baselines/reference/noInferRedeclaration.symbols @@ -0,0 +1,26 @@ +//// [tests/cases/conformance/types/typeRelationships/typeInference/noInferRedeclaration.ts] //// + +=== a.ts === +export const f = (x: T, y: NoInfer) => x; +>f : Symbol(f, Decl(a.ts, 0, 12)) +>T : Symbol(T, Decl(a.ts, 0, 18)) +>x : Symbol(x, Decl(a.ts, 0, 21)) +>T : Symbol(T, Decl(a.ts, 0, 18)) +>y : Symbol(y, Decl(a.ts, 0, 26)) +>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(a.ts, 0, 18)) +>x : Symbol(x, Decl(a.ts, 0, 21)) + +=== b.ts === +import { f } from "./a"; +>f : Symbol(f, Decl(b.ts, 0, 8)) + +type NoInfer = T & number; +>NoInfer : Symbol(NoInfer, Decl(b.ts, 0, 24)) +>T : Symbol(T, Decl(b.ts, 2, 13)) +>T : Symbol(T, Decl(b.ts, 2, 13)) + +export const g = f; +>g : Symbol(g, Decl(b.ts, 4, 12)) +>f : Symbol(f, Decl(b.ts, 0, 8)) + diff --git a/tests/baselines/reference/noInferRedeclaration.types b/tests/baselines/reference/noInferRedeclaration.types new file mode 100644 index 0000000000000..0d8f54db5e4ef --- /dev/null +++ b/tests/baselines/reference/noInferRedeclaration.types @@ -0,0 +1,21 @@ +//// [tests/cases/conformance/types/typeRelationships/typeInference/noInferRedeclaration.ts] //// + +=== a.ts === +export const f = (x: T, y: NoInfer) => x; +>f : (x: T, y: NoInfer) => T +>(x: T, y: NoInfer) => x : (x: T, y: NoInfer) => T +>x : T +>y : NoInfer +>x : T + +=== b.ts === +import { f } from "./a"; +>f : (x: T, y: globalThis.NoInfer) => T + +type NoInfer = T & number; +>NoInfer : NoInfer + +export const g = f; +>g : (x: T, y: globalThis.NoInfer) => T +>f : (x: T, y: globalThis.NoInfer) => T + diff --git a/tests/baselines/reference/tsserver/fourslashServer/autoImportProvider_namespaceSameNameAsIntrinsic.js b/tests/baselines/reference/tsserver/fourslashServer/autoImportProvider_namespaceSameNameAsIntrinsic.js index f09783085489c..5cfac18b02449 100644 --- a/tests/baselines/reference/tsserver/fourslashServer/autoImportProvider_namespaceSameNameAsIntrinsic.js +++ b/tests/baselines/reference/tsserver/fourslashServer/autoImportProvider_namespaceSameNameAsIntrinsic.js @@ -704,6 +704,12 @@ Info seq [hh:mm:ss:mss] response: "kindModifiers": "declare", "sortText": "15" }, + { + "name": "NoInfer", + "kind": "type", + "kindModifiers": "declare", + "sortText": "15" + }, { "name": "NonNullable", "kind": "type", diff --git a/tests/cases/conformance/types/typeRelationships/typeInference/noInfer.ts b/tests/cases/conformance/types/typeRelationships/typeInference/noInfer.ts new file mode 100644 index 0000000000000..49b3dd087a10b --- /dev/null +++ b/tests/cases/conformance/types/typeRelationships/typeInference/noInfer.ts @@ -0,0 +1,84 @@ +// @strict: true +// @declaration: true + +// NoInfer is erased for primitives + +type T00 = NoInfer; +type T01 = NoInfer; +type T02 = NoInfer; +type T03 = NoInfer<"foo">; +type T04 = NoInfer<`foo${string}`>; +type T05 = NoInfer<`foo${string}` & `${string}bar`>; +type T06 = NoInfer<{}>; + +// NoInfer is preserved for object types + +type T10 = NoInfer; +type T11 = NoInfer<{ x: string }>; + +// NoInfer is erased if it has no effect + +type T20 = NoInfer>; +type T21 = NoInfer & string>; +type T22 = NoInfer & string[]>; + +// keyof NoInfer is transformed into NoInfer + +type T30 = keyof NoInfer<{ a: string, b: string }>; +type T31 = keyof NoInfer; +type T32 = { [K in keyof NoInfer<{ a: string, b: string }>]: K }; + +declare function foo1(a: T, b: NoInfer): void +declare function foo2(a: T, b: NoInfer[]): void +declare function foo3(a: T, b: NoInfer): void +declare function foo4(a: T, b: { x: NoInfer }): void +declare function foo5(a: T, b: NoInfer<{ x: T }>): void + +foo1('foo', 'foo') // ok +foo1('foo', 'bar') // error +foo2('foo', ['bar']) // error +foo3('foo', ['bar']) // error +foo4('foo', { x: 'bar' }) // error +foo5('foo', { x: 'bar' }) // error + +declare class Animal { move(): void } +declare class Dog extends Animal { woof(): void } +declare function doSomething(value: T, getDefault: () => NoInfer): void; + +doSomething(new Animal(), () => new Animal()); // ok +doSomething(new Animal(), () => new Dog()); // ok +doSomething(new Dog(), () => new Animal()); // error + +declare function assertEqual(actual: T, expected: NoInfer): boolean; + +assertEqual({ x: 1 }, { x: 3 }); // ok +const g = { x: 3, y: 2 }; +assertEqual(g, { x: 3 }); // error + +declare function invoke(func: (value: T) => R, value: NoInfer): R; +declare function test(value: { x: number; }): number; + +invoke(test, { x: 1, y: 2 }); // error +test({ x: 1, y: 2 }); // error + +type Component = { props: Props; }; +declare function doWork(Component: Component, props: NoInfer): void; +declare const comp: Component<{ foo: number }>; + +doWork(comp, { foo: 42 }); // ok +doWork(comp, {}); // error + +declare function mutate(callback: (a: NoInfer, b: number) => T): T; +const mutate1 = mutate((a, b) => b); + +declare class ExampleClass {} +class OkClass { + constructor(private clazz: ExampleClass, private _value: NoInfer) {} + + get value(): T { + return this._value; // ok + } +} +class OkClass2 { + constructor(private clazz: ExampleClass, public _value: NoInfer) {} +} diff --git a/tests/cases/conformance/types/typeRelationships/typeInference/noInferRedeclaration.ts b/tests/cases/conformance/types/typeRelationships/typeInference/noInferRedeclaration.ts new file mode 100644 index 0000000000000..feab4ca3df0ea --- /dev/null +++ b/tests/cases/conformance/types/typeRelationships/typeInference/noInferRedeclaration.ts @@ -0,0 +1,13 @@ +// @strict: true +// @declaration: true +// @target: esnext + +// @filename: a.ts +export const f = (x: T, y: NoInfer) => x; + +// @filename: b.ts +import { f } from "./a"; + +type NoInfer = T & number; + +export const g = f;