diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 98c79cd187ab0..0832e1edd8941 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -867,6 +867,7 @@ import { NodeLinks, nodeStartsNewLexicalEnvironment, NodeWithTypeArguments, + NoInferType, NonNullChain, NonNullExpression, not, @@ -1357,18 +1358,18 @@ const enum MinArgumentCountFlags { VoidIsNonOptional = 1 << 1, } -const enum IntrinsicTypeKind { +const enum StringMappingTypeKind { Uppercase, Lowercase, Capitalize, Uncapitalize, } -const intrinsicTypeKinds: ReadonlyMap = new Map(Object.entries({ - Uppercase: IntrinsicTypeKind.Uppercase, - Lowercase: IntrinsicTypeKind.Lowercase, - Capitalize: IntrinsicTypeKind.Capitalize, - Uncapitalize: IntrinsicTypeKind.Uncapitalize, +const stringMappingTypeKinds: ReadonlyMap = new Map(Object.entries({ + Uppercase: StringMappingTypeKind.Uppercase, + Lowercase: StringMappingTypeKind.Lowercase, + Capitalize: StringMappingTypeKind.Capitalize, + Uncapitalize: StringMappingTypeKind.Uncapitalize, })); const SymbolLinks = class implements SymbolLinks { @@ -1940,7 +1941,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { var enumLiteralTypes = new Map(); var indexedAccessTypes = new Map(); var templateLiteralTypes = new Map(); - var stringMappingTypes = new Map(); + var intrinsicWrapperTypes = new Map(); var substitutionTypes = new Map(); var subtypeReductionCache = new Map(); var decoratorContextOverrideTypeCache = new Map(); @@ -6699,6 +6700,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const typeNode = typeToTypeNodeHelper((type as StringMappingType).type, context); return symbolToTypeNode((type as StringMappingType).symbol, context, SymbolFlags.Type, [typeNode]); } + if (type.flags & TypeFlags.NoInfer) { + const typeNode = typeToTypeNodeHelper((type as NoInferType).type, context); + return symbolToTypeNode((type as NoInferType).symbol, context, SymbolFlags.Type, [typeNode]); + } if (type.flags & TypeFlags.IndexedAccess) { const objectTypeNode = typeToTypeNodeHelper((type as IndexedAccessType).objectType, context); const indexTypeNode = typeToTypeNodeHelper((type as IndexedAccessType).indexType, context); @@ -14355,6 +14360,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const constraint = getBaseConstraint((t as StringMappingType).type); return constraint && constraint !== (t as StringMappingType).type ? getStringMappingType((t as StringMappingType).symbol, constraint) : stringType; } + if (t.flags & TypeFlags.NoInfer) { + return getBaseConstraint((t as NoInferType).type); + } if (t.flags & TypeFlags.IndexedAccess) { if (isMappedTypeGenericIndexedAccess(t)) { // For indexed access types of the form { [P in K]: E }[X], where K is non-generic and X is generic, @@ -15866,8 +15874,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 && typeArguments && typeArguments.length === 1) { + if (stringMappingTypeKinds.has(symbol.escapedName as string)) { + return getStringMappingType(symbol, typeArguments[0]); + } + return getNoInferType(symbol, typeArguments[0]); } const links = getSymbolLinks(symbol); const typeParameters = links.typeParameters!; @@ -17884,29 +17895,41 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { type; } + function getNoInferType(symbol: Symbol, type: Type): Type { + if (!isGenericType(type)) { + return type; + } + const id = `${getSymbolId(symbol)},${getTypeId(type)}`; + let result = intrinsicWrapperTypes.get(id); + if (!result) { + intrinsicWrapperTypes.set(id, result = createNoInferType(symbol, type)); + } + return result; + } + function applyStringMapping(symbol: Symbol, str: string) { - switch (intrinsicTypeKinds.get(symbol.escapedName as string)) { - case IntrinsicTypeKind.Uppercase: + switch (stringMappingTypeKinds.get(symbol.escapedName as string)) { + case StringMappingTypeKind.Uppercase: return str.toUpperCase(); - case IntrinsicTypeKind.Lowercase: + case StringMappingTypeKind.Lowercase: return str.toLowerCase(); - case IntrinsicTypeKind.Capitalize: + case StringMappingTypeKind.Capitalize: return str.charAt(0).toUpperCase() + str.slice(1); - case IntrinsicTypeKind.Uncapitalize: + case StringMappingTypeKind.Uncapitalize: return str.charAt(0).toLowerCase() + str.slice(1); } return str; } function applyTemplateStringMapping(symbol: Symbol, texts: readonly string[], types: readonly Type[]): [texts: readonly string[], types: readonly Type[]] { - switch (intrinsicTypeKinds.get(symbol.escapedName as string)) { - case IntrinsicTypeKind.Uppercase: + switch (stringMappingTypeKinds.get(symbol.escapedName as string)) { + case StringMappingTypeKind.Uppercase: return [texts.map(t => t.toUpperCase()), types.map(t => getStringMappingType(symbol, t))]; - case IntrinsicTypeKind.Lowercase: + case StringMappingTypeKind.Lowercase: return [texts.map(t => t.toLowerCase()), types.map(t => getStringMappingType(symbol, t))]; - case IntrinsicTypeKind.Capitalize: + case StringMappingTypeKind.Capitalize: return [texts[0] === "" ? texts : [texts[0].charAt(0).toUpperCase() + texts[0].slice(1), ...texts.slice(1)], texts[0] === "" ? [getStringMappingType(symbol, types[0]), ...types.slice(1)] : types]; - case IntrinsicTypeKind.Uncapitalize: + case StringMappingTypeKind.Uncapitalize: return [texts[0] === "" ? texts : [texts[0].charAt(0).toLowerCase() + texts[0].slice(1), ...texts.slice(1)], texts[0] === "" ? [getStringMappingType(symbol, types[0]), ...types.slice(1)] : types]; } return [texts, types]; @@ -17914,9 +17937,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function getStringMappingTypeForGenericType(symbol: Symbol, type: Type): Type { const id = `${getSymbolId(symbol)},${getTypeId(type)}`; - let result = stringMappingTypes.get(id); + let result = intrinsicWrapperTypes.get(id); if (!result) { - stringMappingTypes.set(id, result = createStringMappingType(symbol, type)); + intrinsicWrapperTypes.set(id, result = createStringMappingType(symbol, type)); } return result; } @@ -17927,6 +17950,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return result; } + function createNoInferType(symbol: Symbol, type: Type) { + const result = createTypeWithSymbol(TypeFlags.NoInfer, symbol) as NoInferType; + result.type = type; + return result; + } + function createIndexedAccessType(objectType: Type, indexType: Type, accessFlags: AccessFlags, aliasSymbol: Symbol | undefined, aliasTypeArguments: readonly Type[] | undefined) { const type = createType(TypeFlags.IndexedAccess) as IndexedAccessType; type.objectType = objectType; @@ -19810,6 +19839,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (flags & TypeFlags.StringMapping) { return getStringMappingType((type as StringMappingType).symbol, instantiateType((type as StringMappingType).type, mapper)); } + if (flags & TypeFlags.NoInfer) { + return getNoInferType((type as NoInferType).symbol, instantiateType((type as NoInferType).type, mapper)); + } if (flags & TypeFlags.IndexedAccess) { const newAliasSymbol = aliasSymbol || type.aliasSymbol; const newAliasTypeArguments = aliasSymbol ? aliasTypeArguments : instantiateTypes(type.aliasTypeArguments, mapper); @@ -20980,6 +21012,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { type.flags & TypeFlags.UnionOrIntersection ? getNormalizedUnionOrIntersectionType(type as UnionOrIntersectionType, writing) : type.flags & TypeFlags.Substitution ? writing ? (type as SubstitutionType).baseType : getSubstitutionIntersection(type as SubstitutionType) : type.flags & TypeFlags.Simplifiable ? getSimplifiedType(type, writing) : + type.flags & TypeFlags.NoInfer ? (type as NoInferType).type : type; if (t === type) return t; type = t; @@ -25279,7 +25312,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { inferFromTypes(originalSource, originalTarget); function inferFromTypes(source: Type, target: Type): void { - if (!couldContainTypeVariables(target)) { + if (!couldContainTypeVariables(target) || source.flags & TypeFlags.NoInfer) { return; } if (source === wildcardType || source === blockedStringType) { @@ -44993,7 +45026,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { checkExportsOnMergedDeclarations(node); checkTypeParameters(node.typeParameters); if (node.type.kind === SyntaxKind.IntrinsicKeyword) { - if (!intrinsicTypeKinds.has(node.name.escapedText as string) || length(node.typeParameters) !== 1) { + if (!stringMappingTypeKinds.has(node.name.escapedText as string) && (node.name.escapedText as string) !== "NoInfer" || length(node.typeParameters) !== 1) { error(node.type, Diagnostics.The_intrinsic_keyword_can_only_be_used_to_declare_compiler_provided_intrinsic_types); } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index f430e27b8539d..22acf071fcc7c 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -6093,6 +6093,7 @@ export const enum TypeFlags { NonPrimitive = 1 << 26, // intrinsic object type TemplateLiteral = 1 << 27, // Template literal type StringMapping = 1 << 28, // Uppercase/Lowercase type + NoInfer = 1 << 29, // NoInfer type /** @internal */ AnyOrUnknown = Any | Unknown, @@ -6125,7 +6126,7 @@ export const enum TypeFlags { UnionOrIntersection = Union | Intersection, StructuredType = Object | Union | Intersection, TypeVariable = TypeParameter | IndexedAccess, - InstantiableNonPrimitive = TypeVariable | Conditional | Substitution, + InstantiableNonPrimitive = TypeVariable | Conditional | Substitution | NoInfer, InstantiablePrimitive = Index | TemplateLiteral | StringMapping, Instantiable = InstantiableNonPrimitive | InstantiablePrimitive, StructuredOrInstantiable = StructuredType | Instantiable, @@ -6688,6 +6689,11 @@ export interface StringMappingType extends InstantiableType { type: Type; } +export interface NoInferType extends InstantiableType { + symbol: Symbol; + type: Type; +} + // Type parameter substitution (TypeFlags.Substitution) // 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 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..5875d9f12dd0b 100644 --- a/src/lib/es5.d.ts +++ b/src/lib/es5.d.ts @@ -1648,6 +1648,11 @@ type Capitalize = intrinsic; */ type Uncapitalize = intrinsic; +/** + * Blocks the contained type from participating in type inference. + */ +type NoInfer = intrinsic; + /** * Marker for contextual 'this' type */ diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index a55b003f6c8db..0cb64af2fd49d 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -7107,6 +7107,7 @@ declare namespace ts { NonPrimitive = 67108864, TemplateLiteral = 134217728, StringMapping = 268435456, + NoInfer = 536870912, Literal = 2944, Unit = 109472, Freshable = 2976, @@ -7122,11 +7123,11 @@ declare namespace ts { UnionOrIntersection = 3145728, StructuredType = 3670016, TypeVariable = 8650752, - InstantiableNonPrimitive = 58982400, + InstantiableNonPrimitive = 595853312, InstantiablePrimitive = 406847488, - Instantiable = 465829888, - StructuredOrInstantiable = 469499904, - Narrowable = 536624127, + Instantiable = 1002700800, + StructuredOrInstantiable = 1006370816, + Narrowable = 1073495039, } type DestructuringPattern = BindingPattern | ObjectLiteralExpression | ArrayLiteralExpression; interface Type { @@ -7321,6 +7322,10 @@ declare namespace ts { symbol: Symbol; type: Type; } + interface NoInferType extends InstantiableType { + symbol: Symbol; + type: Type; + } interface SubstitutionType extends InstantiableType { objectFlags: ObjectFlags; baseType: Type; diff --git a/tests/baselines/reference/noInfer.errors.txt b/tests/baselines/reference/noInfer.errors.txt new file mode 100644 index 0000000000000..caaef5ce119cf --- /dev/null +++ b/tests/baselines/reference/noInfer.errors.txt @@ -0,0 +1,80 @@ +noInfer.ts(4,12): error TS2345: Argument of type '"bar"' is not assignable to parameter of type '"foo"'. +noInfer.ts(12,30): error TS2741: Property 'woof' is missing in type 'Animal' but required in type 'Dog'. +noInfer.ts(18,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(23,22): error TS2345: Argument of type '{ x: number; y: number; }' is not assignable to parameter of type '{ x: number; }'. + Object literal may only specify known properties, and 'y' does not exist in type '{ x: number; }'. +noInfer.ts(24,14): error TS2345: Argument of type '{ x: number; y: number; }' is not assignable to parameter of type '{ x: number; }'. + Object literal may only specify known properties, and 'y' does not exist in type '{ x: number; }'. +noInfer.ts(31,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 (6 errors) ==== + export declare function foo(a: T, b: NoInfer): void + + foo('foo', 'foo') // ok + foo('foo', 'bar') // error + ~~~~~ +!!! error TS2345: Argument of type '"bar"' is not assignable to parameter of type '"foo"'. + + export declare class Animal { move(): void } + export declare class Dog extends Animal { woof(): void } + export 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:7:43: 'woof' is declared here. +!!! related TS6502 noInfer.ts:8:62: The expected type comes from the return type of this signature. + + export 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:17:19: 'y' is declared here. + + export declare function invoke(func: (value: T) => R, value: NoInfer): R; + export declare function test(value: { x: number; }): number; + + invoke(test, { x: 1, y: 2 }); // error + ~ +!!! error TS2345: Argument of type '{ x: number; y: number; }' is not assignable to parameter of type '{ x: number; }'. +!!! error TS2345: Object literal may only specify known properties, and 'y' does not exist in type '{ x: number; }'. + test({ x: 1, y: 2 }); // error + ~ +!!! error TS2345: Argument of type '{ x: number; y: number; }' is not assignable to parameter of type '{ x: number; }'. +!!! error TS2345: Object literal may only specify known properties, and 'y' does not exist in type '{ x: number; }'. + + export type Component = { props: Props; }; + export declare function doWork(Component: Component, props: NoInfer): void; + export 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:28:40: 'foo' is declared here. + + export declare function mutate(callback: (a: NoInfer, b: number) => T): T; + export const mutate1 = mutate((a, b) => b); + + export declare class ExampleClass {} + export class OkClass { + constructor(private clazz: ExampleClass, private _value: NoInfer) {} + + get value(): T { + return this._value; // ok + } + } + export 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..ba1ed4b171100 --- /dev/null +++ b/tests/baselines/reference/noInfer.js @@ -0,0 +1,122 @@ +//// [tests/cases/conformance/types/typeRelationships/typeInference/noInfer.ts] //// + +//// [noInfer.ts] +export declare function foo(a: T, b: NoInfer): void + +foo('foo', 'foo') // ok +foo('foo', 'bar') // error + +export declare class Animal { move(): void } +export declare class Dog extends Animal { woof(): void } +export 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 + +export 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 + +export declare function invoke(func: (value: T) => R, value: NoInfer): R; +export declare function test(value: { x: number; }): number; + +invoke(test, { x: 1, y: 2 }); // error +test({ x: 1, y: 2 }); // error + +export type Component = { props: Props; }; +export declare function doWork(Component: Component, props: NoInfer): void; +export declare const comp: Component<{ foo: number }>; + +doWork(comp, { foo: 42 }); // ok +doWork(comp, {}); // error + +export declare function mutate(callback: (a: NoInfer, b: number) => T): T; +export const mutate1 = mutate((a, b) => b); + +export declare class ExampleClass {} +export class OkClass { + constructor(private clazz: ExampleClass, private _value: NoInfer) {} + + get value(): T { + return this._value; // ok + } +} +export class OkClass2 { + constructor(private clazz: ExampleClass, public _value: NoInfer) {} +} + + +//// [noInfer.js] +foo('foo', 'foo'); // ok +foo('foo', 'bar'); // error +doSomething(new Animal(), () => new Animal()); // ok +doSomething(new Animal(), () => new Dog()); // ok +doSomething(new Dog(), () => new Animal()); // error +assertEqual({ x: 1 }, { x: 3 }); // ok +const 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 +export const mutate1 = mutate((a, b) => b); +export class OkClass { + clazz; + _value; + constructor(clazz, _value) { + this.clazz = clazz; + this._value = _value; + } + get value() { + return this._value; // ok + } +} +export class OkClass2 { + clazz; + _value; + constructor(clazz, _value) { + this.clazz = clazz; + this._value = _value; + } +} + + +//// [noInfer.d.ts] +export declare function foo(a: T, b: NoInfer): void; +export declare class Animal { + move(): void; +} +export declare class Dog extends Animal { + woof(): void; +} +export declare function doSomething(value: T, getDefault: () => NoInfer): void; +export declare function assertEqual(actual: T, expected: NoInfer): boolean; +export declare function invoke(func: (value: T) => R, value: NoInfer): R; +export declare function test(value: { + x: number; +}): number; +export type Component = { + props: Props; +}; +export declare function doWork(Component: Component, props: NoInfer): void; +export declare const comp: Component<{ + foo: number; +}>; +export declare function mutate(callback: (a: NoInfer, b: number) => T): T; +export declare const mutate1: unknown; +export declare class ExampleClass { +} +export declare class OkClass { + private clazz; + private _value; + constructor(clazz: ExampleClass, _value: NoInfer); + get value(): T; +} +export 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..8403917c1209a --- /dev/null +++ b/tests/baselines/reference/noInfer.symbols @@ -0,0 +1,191 @@ +//// [tests/cases/conformance/types/typeRelationships/typeInference/noInfer.ts] //// + +=== noInfer.ts === +export declare function foo(a: T, b: NoInfer): void +>foo : Symbol(foo, Decl(noInfer.ts, 0, 0)) +>T : Symbol(T, Decl(noInfer.ts, 0, 28)) +>a : Symbol(a, Decl(noInfer.ts, 0, 46)) +>T : Symbol(T, Decl(noInfer.ts, 0, 28)) +>b : Symbol(b, Decl(noInfer.ts, 0, 51)) +>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(noInfer.ts, 0, 28)) + +foo('foo', 'foo') // ok +>foo : Symbol(foo, Decl(noInfer.ts, 0, 0)) + +foo('foo', 'bar') // error +>foo : Symbol(foo, Decl(noInfer.ts, 0, 0)) + +export declare class Animal { move(): void } +>Animal : Symbol(Animal, Decl(noInfer.ts, 3, 17)) +>move : Symbol(Animal.move, Decl(noInfer.ts, 5, 29)) + +export declare class Dog extends Animal { woof(): void } +>Dog : Symbol(Dog, Decl(noInfer.ts, 5, 44)) +>Animal : Symbol(Animal, Decl(noInfer.ts, 3, 17)) +>woof : Symbol(Dog.woof, Decl(noInfer.ts, 6, 41)) + +export declare function doSomething(value: T, getDefault: () => NoInfer): void; +>doSomething : Symbol(doSomething, Decl(noInfer.ts, 6, 56)) +>T : Symbol(T, Decl(noInfer.ts, 7, 36)) +>value : Symbol(value, Decl(noInfer.ts, 7, 39)) +>T : Symbol(T, Decl(noInfer.ts, 7, 36)) +>getDefault : Symbol(getDefault, Decl(noInfer.ts, 7, 48)) +>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(noInfer.ts, 7, 36)) + +doSomething(new Animal(), () => new Animal()); // ok +>doSomething : Symbol(doSomething, Decl(noInfer.ts, 6, 56)) +>Animal : Symbol(Animal, Decl(noInfer.ts, 3, 17)) +>Animal : Symbol(Animal, Decl(noInfer.ts, 3, 17)) + +doSomething(new Animal(), () => new Dog()); // ok +>doSomething : Symbol(doSomething, Decl(noInfer.ts, 6, 56)) +>Animal : Symbol(Animal, Decl(noInfer.ts, 3, 17)) +>Dog : Symbol(Dog, Decl(noInfer.ts, 5, 44)) + +doSomething(new Dog(), () => new Animal()); // error +>doSomething : Symbol(doSomething, Decl(noInfer.ts, 6, 56)) +>Dog : Symbol(Dog, Decl(noInfer.ts, 5, 44)) +>Animal : Symbol(Animal, Decl(noInfer.ts, 3, 17)) + +export declare function assertEqual(actual: T, expected: NoInfer): boolean; +>assertEqual : Symbol(assertEqual, Decl(noInfer.ts, 11, 43)) +>T : Symbol(T, Decl(noInfer.ts, 13, 36)) +>actual : Symbol(actual, Decl(noInfer.ts, 13, 39)) +>T : Symbol(T, Decl(noInfer.ts, 13, 36)) +>expected : Symbol(expected, Decl(noInfer.ts, 13, 49)) +>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(noInfer.ts, 13, 36)) + +assertEqual({ x: 1 }, { x: 3 }); // ok +>assertEqual : Symbol(assertEqual, Decl(noInfer.ts, 11, 43)) +>x : Symbol(x, Decl(noInfer.ts, 15, 13)) +>x : Symbol(x, Decl(noInfer.ts, 15, 23)) + +const g = { x: 3, y: 2 }; +>g : Symbol(g, Decl(noInfer.ts, 16, 5)) +>x : Symbol(x, Decl(noInfer.ts, 16, 11)) +>y : Symbol(y, Decl(noInfer.ts, 16, 17)) + +assertEqual(g, { x: 3 }); // error +>assertEqual : Symbol(assertEqual, Decl(noInfer.ts, 11, 43)) +>g : Symbol(g, Decl(noInfer.ts, 16, 5)) +>x : Symbol(x, Decl(noInfer.ts, 17, 16)) + +export declare function invoke(func: (value: T) => R, value: NoInfer): R; +>invoke : Symbol(invoke, Decl(noInfer.ts, 17, 25)) +>T : Symbol(T, Decl(noInfer.ts, 19, 31)) +>R : Symbol(R, Decl(noInfer.ts, 19, 33)) +>func : Symbol(func, Decl(noInfer.ts, 19, 37)) +>value : Symbol(value, Decl(noInfer.ts, 19, 44)) +>T : Symbol(T, Decl(noInfer.ts, 19, 31)) +>R : Symbol(R, Decl(noInfer.ts, 19, 33)) +>value : Symbol(value, Decl(noInfer.ts, 19, 59)) +>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(noInfer.ts, 19, 31)) +>R : Symbol(R, Decl(noInfer.ts, 19, 33)) + +export declare function test(value: { x: number; }): number; +>test : Symbol(test, Decl(noInfer.ts, 19, 82)) +>value : Symbol(value, Decl(noInfer.ts, 20, 29)) +>x : Symbol(x, Decl(noInfer.ts, 20, 37)) + +invoke(test, { x: 1, y: 2 }); // error +>invoke : Symbol(invoke, Decl(noInfer.ts, 17, 25)) +>test : Symbol(test, Decl(noInfer.ts, 19, 82)) +>x : Symbol(x, Decl(noInfer.ts, 22, 14)) +>y : Symbol(y, Decl(noInfer.ts, 22, 20)) + +test({ x: 1, y: 2 }); // error +>test : Symbol(test, Decl(noInfer.ts, 19, 82)) +>x : Symbol(x, Decl(noInfer.ts, 23, 6)) +>y : Symbol(y, Decl(noInfer.ts, 23, 12)) + +export type Component = { props: Props; }; +>Component : Symbol(Component, Decl(noInfer.ts, 23, 21)) +>Props : Symbol(Props, Decl(noInfer.ts, 25, 22)) +>props : Symbol(props, Decl(noInfer.ts, 25, 32)) +>Props : Symbol(Props, Decl(noInfer.ts, 25, 22)) + +export declare function doWork(Component: Component, props: NoInfer): void; +>doWork : Symbol(doWork, Decl(noInfer.ts, 25, 49)) +>Props : Symbol(Props, Decl(noInfer.ts, 26, 31)) +>Component : Symbol(Component, Decl(noInfer.ts, 26, 38)) +>Component : Symbol(Component, Decl(noInfer.ts, 23, 21)) +>Props : Symbol(Props, Decl(noInfer.ts, 26, 31)) +>props : Symbol(props, Decl(noInfer.ts, 26, 66)) +>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --)) +>Props : Symbol(Props, Decl(noInfer.ts, 26, 31)) + +export declare const comp: Component<{ foo: number }>; +>comp : Symbol(comp, Decl(noInfer.ts, 27, 20)) +>Component : Symbol(Component, Decl(noInfer.ts, 23, 21)) +>foo : Symbol(foo, Decl(noInfer.ts, 27, 38)) + +doWork(comp, { foo: 42 }); // ok +>doWork : Symbol(doWork, Decl(noInfer.ts, 25, 49)) +>comp : Symbol(comp, Decl(noInfer.ts, 27, 20)) +>foo : Symbol(foo, Decl(noInfer.ts, 29, 14)) + +doWork(comp, {}); // error +>doWork : Symbol(doWork, Decl(noInfer.ts, 25, 49)) +>comp : Symbol(comp, Decl(noInfer.ts, 27, 20)) + +export declare function mutate(callback: (a: NoInfer, b: number) => T): T; +>mutate : Symbol(mutate, Decl(noInfer.ts, 30, 17)) +>T : Symbol(T, Decl(noInfer.ts, 32, 31)) +>callback : Symbol(callback, Decl(noInfer.ts, 32, 34)) +>a : Symbol(a, Decl(noInfer.ts, 32, 45)) +>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(noInfer.ts, 32, 31)) +>b : Symbol(b, Decl(noInfer.ts, 32, 59)) +>T : Symbol(T, Decl(noInfer.ts, 32, 31)) +>T : Symbol(T, Decl(noInfer.ts, 32, 31)) + +export const mutate1 = mutate((a, b) => b); +>mutate1 : Symbol(mutate1, Decl(noInfer.ts, 33, 12)) +>mutate : Symbol(mutate, Decl(noInfer.ts, 30, 17)) +>a : Symbol(a, Decl(noInfer.ts, 33, 31)) +>b : Symbol(b, Decl(noInfer.ts, 33, 33)) +>b : Symbol(b, Decl(noInfer.ts, 33, 33)) + +export declare class ExampleClass {} +>ExampleClass : Symbol(ExampleClass, Decl(noInfer.ts, 33, 43)) +>T : Symbol(T, Decl(noInfer.ts, 35, 34)) + +export class OkClass { +>OkClass : Symbol(OkClass, Decl(noInfer.ts, 35, 39)) +>T : Symbol(T, Decl(noInfer.ts, 36, 21)) + + constructor(private clazz: ExampleClass, private _value: NoInfer) {} +>clazz : Symbol(OkClass.clazz, Decl(noInfer.ts, 37, 16)) +>ExampleClass : Symbol(ExampleClass, Decl(noInfer.ts, 33, 43)) +>T : Symbol(T, Decl(noInfer.ts, 36, 21)) +>_value : Symbol(OkClass._value, Decl(noInfer.ts, 37, 47)) +>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(noInfer.ts, 36, 21)) + + get value(): T { +>value : Symbol(OkClass.value, Decl(noInfer.ts, 37, 78)) +>T : Symbol(T, Decl(noInfer.ts, 36, 21)) + + return this._value; // ok +>this._value : Symbol(OkClass._value, Decl(noInfer.ts, 37, 47)) +>this : Symbol(OkClass, Decl(noInfer.ts, 35, 39)) +>_value : Symbol(OkClass._value, Decl(noInfer.ts, 37, 47)) + } +} +export class OkClass2 { +>OkClass2 : Symbol(OkClass2, Decl(noInfer.ts, 42, 1)) +>T : Symbol(T, Decl(noInfer.ts, 43, 22)) + + constructor(private clazz: ExampleClass, public _value: NoInfer) {} +>clazz : Symbol(OkClass2.clazz, Decl(noInfer.ts, 44, 16)) +>ExampleClass : Symbol(ExampleClass, Decl(noInfer.ts, 33, 43)) +>T : Symbol(T, Decl(noInfer.ts, 43, 22)) +>_value : Symbol(OkClass2._value, Decl(noInfer.ts, 44, 47)) +>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(noInfer.ts, 43, 22)) +} + diff --git a/tests/baselines/reference/noInfer.types b/tests/baselines/reference/noInfer.types new file mode 100644 index 0000000000000..67128d67d95e5 --- /dev/null +++ b/tests/baselines/reference/noInfer.types @@ -0,0 +1,191 @@ +//// [tests/cases/conformance/types/typeRelationships/typeInference/noInfer.ts] //// + +=== noInfer.ts === +export declare function foo(a: T, b: NoInfer): void +>foo : (a: T, b: NoInfer) => void +>a : T +>b : NoInfer + +foo('foo', 'foo') // ok +>foo('foo', 'foo') : void +>foo : (a: T, b: NoInfer) => void +>'foo' : "foo" +>'foo' : "foo" + +foo('foo', 'bar') // error +>foo('foo', 'bar') : void +>foo : (a: T, b: NoInfer) => void +>'foo' : "foo" +>'bar' : "bar" + +export declare class Animal { move(): void } +>Animal : Animal +>move : () => void + +export declare class Dog extends Animal { woof(): void } +>Dog : Dog +>Animal : Animal +>woof : () => void + +export 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 + +export 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 + +export 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 + +export 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 + +export type Component = { props: Props; }; +>Component : Component +>props : Props + +export declare function doWork(Component: Component, props: NoInfer): void; +>doWork : (Component: Component, props: NoInfer) => void +>Component : Component +>props : NoInfer + +export 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; }> +>{} : {} + +export 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 + +export 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 + +export declare class ExampleClass {} +>ExampleClass : ExampleClass + +export 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 + } +} +export class OkClass2 { +>OkClass2 : OkClass2 + + constructor(private clazz: ExampleClass, public _value: NoInfer) {} +>clazz : ExampleClass +>_value : NoInfer +} + 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..9d3fbd6f666c2 --- /dev/null +++ b/tests/cases/conformance/types/typeRelationships/typeInference/noInfer.ts @@ -0,0 +1,50 @@ +// @strict: true +// @target: esnext +// @declaration: true + +export declare function foo(a: T, b: NoInfer): void + +foo('foo', 'foo') // ok +foo('foo', 'bar') // error + +export declare class Animal { move(): void } +export declare class Dog extends Animal { woof(): void } +export 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 + +export 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 + +export declare function invoke(func: (value: T) => R, value: NoInfer): R; +export declare function test(value: { x: number; }): number; + +invoke(test, { x: 1, y: 2 }); // error +test({ x: 1, y: 2 }); // error + +export type Component = { props: Props; }; +export declare function doWork(Component: Component, props: NoInfer): void; +export declare const comp: Component<{ foo: number }>; + +doWork(comp, { foo: 42 }); // ok +doWork(comp, {}); // error + +export declare function mutate(callback: (a: NoInfer, b: number) => T): T; +export const mutate1 = mutate((a, b) => b); + +export declare class ExampleClass {} +export class OkClass { + constructor(private clazz: ExampleClass, private _value: NoInfer) {} + + get value(): T { + return this._value; // ok + } +} +export class OkClass2 { + constructor(private clazz: ExampleClass, public _value: NoInfer) {} +}