From df7ed82dd5a05c6c13b6380cd39c336b1dab9b0e Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 28 Mar 2022 10:44:11 -0700 Subject: [PATCH] Revise accessor resolution logic and error reporting (#48459) * Revise accessor resolution logic and error reporting * Accept new baselines * Update isTypeElement * Add tests --- src/compiler/checker.ts | 192 +++++++----------- src/compiler/utilitiesPublic.ts | 4 +- .../circularAccessorAnnotations.errors.txt | 41 ++++ .../reference/circularAccessorAnnotations.js | 53 +++++ .../circularAccessorAnnotations.symbols | 65 ++++++ .../circularAccessorAnnotations.types | 62 ++++++ .../circularIndexedAccessErrors.errors.txt | 6 +- .../limitDeepInstantiations.errors.txt | 6 +- .../compiler/circularAccessorAnnotations.ts | 28 +++ 9 files changed, 330 insertions(+), 127 deletions(-) create mode 100644 tests/baselines/reference/circularAccessorAnnotations.errors.txt create mode 100644 tests/baselines/reference/circularAccessorAnnotations.js create mode 100644 tests/baselines/reference/circularAccessorAnnotations.symbols create mode 100644 tests/baselines/reference/circularAccessorAnnotations.types create mode 100644 tests/cases/compiler/circularAccessorAnnotations.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4fdc647fb2f58..1cd7e00d0dd6c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -172,6 +172,7 @@ namespace ts { EnumTagType, ResolvedTypeArguments, ResolvedBaseTypes, + WriteType, } const enum CheckMode { @@ -8519,6 +8520,8 @@ namespace ts { return !!(target as TypeReference).resolvedTypeArguments; case TypeSystemPropertyName.ResolvedBaseTypes: return !!(target as InterfaceType).baseTypesResolved; + case TypeSystemPropertyName.WriteType: + return !!getSymbolLinks(target as Symbol).writeType; } return Debug.assertNever(propertyName); } @@ -9502,6 +9505,11 @@ namespace ts { } return getWidenedType(getWidenedLiteralType(checkExpression(declaration.statements[0].expression))); } + if (isAccessor(declaration)) { + // Binding of certain patterns in JS code will occasionally mark symbols as both properties + // and accessors. Here we dispatch to accessor resolution if needed. + return getTypeOfAccessors(symbol); + } // Handle variable, parameter or property if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) { @@ -9567,9 +9575,6 @@ namespace ts { else if (isEnumMember(declaration)) { type = getTypeOfEnumMember(symbol); } - else if (isAccessor(declaration)) { - type = resolveTypeOfAccessors(symbol) || Debug.fail("Non-write accessor resolution must always produce a type"); - } else { return Debug.fail("Unhandled declaration kind! " + Debug.formatSyntaxKind(declaration.kind) + " for " + Debug.formatSymbol(symbol)); } @@ -9614,97 +9619,62 @@ namespace ts { function getTypeOfAccessors(symbol: Symbol): Type { const links = getSymbolLinks(symbol); - return links.type || (links.type = getTypeOfAccessorsWorker(symbol) || Debug.fail("Read type of accessor must always produce a type")); - } - - function getTypeOfSetAccessor(symbol: Symbol): Type | undefined { - const links = getSymbolLinks(symbol); - return links.writeType || (links.writeType = getTypeOfAccessorsWorker(symbol, /*writing*/ true)); - } - - function getTypeOfAccessorsWorker(symbol: Symbol, writing = false): Type | undefined { - if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) { - return errorType; - } - - let type = resolveTypeOfAccessors(symbol, writing); - if (!popTypeResolution()) { + if (!links.type) { + if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) { + return errorType; + } const getter = getDeclarationOfKind(symbol, SyntaxKind.GetAccessor); - if (getter) { - if (getEffectiveTypeAnnotationNode(getter)) { - error(getter.name, Diagnostics._0_is_referenced_directly_or_indirectly_in_its_own_type_annotation, symbolToString(symbol)); + const setter = getDeclarationOfKind(symbol, SyntaxKind.SetAccessor); + // We try to resolve a getter type annotation, a setter type annotation, or a getter function + // body return type inference, in that order. + let type = getter && isInJSFile(getter) && getTypeForDeclarationFromJSDocComment(getter) || + getAnnotatedAccessorType(getter) || + getAnnotatedAccessorType(setter) || + getter && getter.body && getReturnTypeFromBody(getter); + if (!type) { + if (setter && !isPrivateWithinAmbient(setter)) { + errorOrSuggestion(noImplicitAny, setter, Diagnostics.Property_0_implicitly_has_type_any_because_its_set_accessor_lacks_a_parameter_type_annotation, symbolToString(symbol)); + } + else if (getter && !isPrivateWithinAmbient(getter)) { + errorOrSuggestion(noImplicitAny, getter, Diagnostics.Property_0_implicitly_has_type_any_because_its_get_accessor_lacks_a_return_type_annotation, symbolToString(symbol)); + } + type = anyType; + } + if (!popTypeResolution()) { + if (getAnnotatedAccessorTypeNode(getter)) { + error(getter, Diagnostics._0_is_referenced_directly_or_indirectly_in_its_own_type_annotation, symbolToString(symbol)); } - else if (noImplicitAny) { + else if (getAnnotatedAccessorTypeNode(setter)) { + error(setter, Diagnostics._0_is_referenced_directly_or_indirectly_in_its_own_type_annotation, symbolToString(symbol)); + } + else if (getter && noImplicitAny) { error(getter, Diagnostics._0_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions, symbolToString(symbol)); } + type = anyType; } - type = anyType; + links.type = type; } - return type; + return links.type; } - function resolveTypeOfAccessors(symbol: Symbol, writing = false) { - const getter = getDeclarationOfKind(symbol, SyntaxKind.GetAccessor); - const setter = getDeclarationOfKind(symbol, SyntaxKind.SetAccessor); - - // For write operations, prioritize type annotations on the setter - if (writing) { - const setterType = getAnnotatedAccessorType(setter); - if (setterType) { - return instantiateTypeIfNeeded(setterType, symbol); - } - } - // Else defer to the getter type - - if (getter && isInJSFile(getter)) { - const jsDocType = getTypeForDeclarationFromJSDocComment(getter); - if (jsDocType) { - return instantiateTypeIfNeeded(jsDocType, symbol); - } - } - - // Try to see if the user specified a return type on the get-accessor. - const getterType = getAnnotatedAccessorType(getter); - if (getterType) { - return instantiateTypeIfNeeded(getterType, symbol); - } - - // If the user didn't specify a return type, try to use the set-accessor's parameter type. - const setterType = getAnnotatedAccessorType(setter); - if (setterType) { - return setterType; - } - - // If there are no specified types, try to infer it from the body of the get accessor if it exists. - if (getter && getter.body) { - const returnTypeFromBody = getReturnTypeFromBody(getter); - return instantiateTypeIfNeeded(returnTypeFromBody, symbol); - } - - // Otherwise, fall back to 'any'. - if (setter) { - if (!isPrivateWithinAmbient(setter)) { - errorOrSuggestion(noImplicitAny, setter, Diagnostics.Property_0_implicitly_has_type_any_because_its_set_accessor_lacks_a_parameter_type_annotation, symbolToString(symbol)); - } - return anyType; - } - else if (getter) { - Debug.assert(!!getter, "there must exist a getter as we are current checking either setter or getter in this function"); - if (!isPrivateWithinAmbient(getter)) { - errorOrSuggestion(noImplicitAny, getter, Diagnostics.Property_0_implicitly_has_type_any_because_its_get_accessor_lacks_a_return_type_annotation, symbolToString(symbol)); + function getWriteTypeOfAccessors(symbol: Symbol): Type { + const links = getSymbolLinks(symbol); + if (!links.writeType) { + if (!pushTypeResolution(symbol, TypeSystemPropertyName.WriteType)) { + return errorType; } - return anyType; - } - return undefined; - - function instantiateTypeIfNeeded(type: Type, symbol: Symbol) { - if (getCheckFlags(symbol) & CheckFlags.Instantiated) { - const links = getSymbolLinks(symbol); - return instantiateType(type, links.mapper); + const setter = getDeclarationOfKind(symbol, SyntaxKind.SetAccessor); + let writeType = getAnnotatedAccessorType(setter); + if (!popTypeResolution()) { + if (getAnnotatedAccessorTypeNode(setter)) { + error(setter, Diagnostics._0_is_referenced_directly_or_indirectly_in_its_own_type_annotation, symbolToString(symbol)); + } + writeType = anyType; } - - return type; + // Absent an explicit setter type annotation we use the read type of the accessor. + links.writeType = writeType || getTypeOfAccessors(symbol); } + return links.writeType; } function getBaseTypeVariableOfClass(symbol: Symbol) { @@ -9792,17 +9762,12 @@ namespace ts { function getTypeOfInstantiatedSymbol(symbol: Symbol): Type { const links = getSymbolLinks(symbol); - if (!links.type) { - if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) { - return links.type = errorType; - } - let type = instantiateType(getTypeOfSymbol(links.target!), links.mapper); - if (!popTypeResolution()) { - type = reportCircularityError(symbol); - } - links.type = type; - } - return links.type; + return links.type || (links.type = instantiateType(getTypeOfSymbol(links.target!), links.mapper)); + } + + function getWriteTypeOfInstantiatedSymbol(symbol: Symbol): Type { + const links = getSymbolLinks(symbol); + return links.writeType || (links.writeType = instantiateType(getWriteTypeOfSymbol(links.target!), links.mapper)); } function reportCircularityError(symbol: Symbol) { @@ -9845,36 +9810,23 @@ namespace ts { } /** - * Distinct write types come only from set accessors, but union and intersection - * properties deriving from set accessors will either pre-compute or defer the - * union or intersection of the writeTypes of their constituents. To account for - * this, we will assume that any deferred type or transient symbol may have a - * `writeType` (or a deferred write type ready to be computed) that should be - * used before looking for set accessor declarations. + * Distinct write types come only from set accessors, but synthetic union and intersection + * properties deriving from set accessors will either pre-compute or defer the union or + * intersection of the writeTypes of their constituents. */ function getWriteTypeOfSymbol(symbol: Symbol): Type { const checkFlags = getCheckFlags(symbol); - if (checkFlags & CheckFlags.DeferredType) { - const writeType = getWriteTypeOfSymbolWithDeferredType(symbol); - if (writeType) { - return writeType; - } - } - if (symbol.flags & SymbolFlags.Transient) { - const { writeType } = symbol as TransientSymbol; - if (writeType) { - return writeType; - } + if (symbol.flags & SymbolFlags.Property) { + return checkFlags & CheckFlags.SyntheticProperty ? + checkFlags & CheckFlags.DeferredType ? + getWriteTypeOfSymbolWithDeferredType(symbol) || getTypeOfSymbolWithDeferredType(symbol) : + (symbol as TransientSymbol).writeType || (symbol as TransientSymbol).type! : + getTypeOfSymbol(symbol); } - return getSetAccessorTypeOfSymbol(symbol); - } - - function getSetAccessorTypeOfSymbol(symbol: Symbol): Type { if (symbol.flags & SymbolFlags.Accessor) { - const type = getTypeOfSetAccessor(symbol); - if (type) { - return type; - } + return checkFlags & CheckFlags.Instantiated ? + getWriteTypeOfInstantiatedSymbol(symbol) : + getWriteTypeOfAccessors(symbol); } return getTypeOfSymbol(symbol); } @@ -25237,7 +25189,7 @@ namespace ts { } } if (isDeclarationName(location) && isSetAccessor(location.parent) && getAnnotatedAccessorTypeNode(location.parent)) { - return resolveTypeOfAccessors(location.parent.symbol, /*writing*/ true)!; + return getWriteTypeOfAccessors(location.parent.symbol); } // The location isn't a reference to the given symbol, meaning we're being asked // a hypothetical question of what type the symbol would have if there was a reference diff --git a/src/compiler/utilitiesPublic.ts b/src/compiler/utilitiesPublic.ts index ce8aa92d98d46..ecfb4e52dafca 100644 --- a/src/compiler/utilitiesPublic.ts +++ b/src/compiler/utilitiesPublic.ts @@ -1335,7 +1335,9 @@ namespace ts { || kind === SyntaxKind.CallSignature || kind === SyntaxKind.PropertySignature || kind === SyntaxKind.MethodSignature - || kind === SyntaxKind.IndexSignature; + || kind === SyntaxKind.IndexSignature + || kind === SyntaxKind.GetAccessor + || kind === SyntaxKind.SetAccessor; } export function isClassOrTypeElement(node: Node): node is ClassElement | TypeElement { diff --git a/tests/baselines/reference/circularAccessorAnnotations.errors.txt b/tests/baselines/reference/circularAccessorAnnotations.errors.txt new file mode 100644 index 0000000000000..890726fcaf6f6 --- /dev/null +++ b/tests/baselines/reference/circularAccessorAnnotations.errors.txt @@ -0,0 +1,41 @@ +tests/cases/compiler/circularAccessorAnnotations.ts(2,9): error TS2502: 'foo' is referenced directly or indirectly in its own type annotation. +tests/cases/compiler/circularAccessorAnnotations.ts(6,9): error TS2502: 'foo' is referenced directly or indirectly in its own type annotation. +tests/cases/compiler/circularAccessorAnnotations.ts(15,9): error TS2502: 'foo' is referenced directly or indirectly in its own type annotation. +tests/cases/compiler/circularAccessorAnnotations.ts(19,9): error TS2502: 'foo' is referenced directly or indirectly in its own type annotation. + + +==== tests/cases/compiler/circularAccessorAnnotations.ts (4 errors) ==== + declare const c1: { + get foo(): typeof c1.foo; + ~~~ +!!! error TS2502: 'foo' is referenced directly or indirectly in its own type annotation. + } + + declare const c2: { + set foo(value: typeof c2.foo); + ~~~ +!!! error TS2502: 'foo' is referenced directly or indirectly in its own type annotation. + } + + declare const c3: { + get foo(): string; + set foo(value: typeof c3.foo); + } + + type T1 = { + get foo(): T1["foo"]; + ~~~ +!!! error TS2502: 'foo' is referenced directly or indirectly in its own type annotation. + } + + type T2 = { + set foo(value: T2["foo"]); + ~~~ +!!! error TS2502: 'foo' is referenced directly or indirectly in its own type annotation. + } + + type T3 = { + get foo(): string; + set foo(value: T3["foo"]); + } + \ No newline at end of file diff --git a/tests/baselines/reference/circularAccessorAnnotations.js b/tests/baselines/reference/circularAccessorAnnotations.js new file mode 100644 index 0000000000000..2cdb41d7fa2d5 --- /dev/null +++ b/tests/baselines/reference/circularAccessorAnnotations.js @@ -0,0 +1,53 @@ +//// [circularAccessorAnnotations.ts] +declare const c1: { + get foo(): typeof c1.foo; +} + +declare const c2: { + set foo(value: typeof c2.foo); +} + +declare const c3: { + get foo(): string; + set foo(value: typeof c3.foo); +} + +type T1 = { + get foo(): T1["foo"]; +} + +type T2 = { + set foo(value: T2["foo"]); +} + +type T3 = { + get foo(): string; + set foo(value: T3["foo"]); +} + + +//// [circularAccessorAnnotations.js] +"use strict"; + + +//// [circularAccessorAnnotations.d.ts] +declare const c1: { + get foo(): typeof c1.foo; +}; +declare const c2: { + set foo(value: typeof c2.foo); +}; +declare const c3: { + get foo(): string; + set foo(value: typeof c3.foo); +}; +declare type T1 = { + get foo(): T1["foo"]; +}; +declare type T2 = { + set foo(value: T2["foo"]); +}; +declare type T3 = { + get foo(): string; + set foo(value: T3["foo"]); +}; diff --git a/tests/baselines/reference/circularAccessorAnnotations.symbols b/tests/baselines/reference/circularAccessorAnnotations.symbols new file mode 100644 index 0000000000000..b3514fc1ea494 --- /dev/null +++ b/tests/baselines/reference/circularAccessorAnnotations.symbols @@ -0,0 +1,65 @@ +=== tests/cases/compiler/circularAccessorAnnotations.ts === +declare const c1: { +>c1 : Symbol(c1, Decl(circularAccessorAnnotations.ts, 0, 13)) + + get foo(): typeof c1.foo; +>foo : Symbol(foo, Decl(circularAccessorAnnotations.ts, 0, 19)) +>c1.foo : Symbol(foo, Decl(circularAccessorAnnotations.ts, 0, 19)) +>c1 : Symbol(c1, Decl(circularAccessorAnnotations.ts, 0, 13)) +>foo : Symbol(foo, Decl(circularAccessorAnnotations.ts, 0, 19)) +} + +declare const c2: { +>c2 : Symbol(c2, Decl(circularAccessorAnnotations.ts, 4, 13)) + + set foo(value: typeof c2.foo); +>foo : Symbol(foo, Decl(circularAccessorAnnotations.ts, 4, 19)) +>value : Symbol(value, Decl(circularAccessorAnnotations.ts, 5, 12)) +>c2.foo : Symbol(foo, Decl(circularAccessorAnnotations.ts, 4, 19)) +>c2 : Symbol(c2, Decl(circularAccessorAnnotations.ts, 4, 13)) +>foo : Symbol(foo, Decl(circularAccessorAnnotations.ts, 4, 19)) +} + +declare const c3: { +>c3 : Symbol(c3, Decl(circularAccessorAnnotations.ts, 8, 13)) + + get foo(): string; +>foo : Symbol(foo, Decl(circularAccessorAnnotations.ts, 8, 19), Decl(circularAccessorAnnotations.ts, 9, 22)) + + set foo(value: typeof c3.foo); +>foo : Symbol(foo, Decl(circularAccessorAnnotations.ts, 8, 19), Decl(circularAccessorAnnotations.ts, 9, 22)) +>value : Symbol(value, Decl(circularAccessorAnnotations.ts, 10, 12)) +>c3.foo : Symbol(foo, Decl(circularAccessorAnnotations.ts, 8, 19), Decl(circularAccessorAnnotations.ts, 9, 22)) +>c3 : Symbol(c3, Decl(circularAccessorAnnotations.ts, 8, 13)) +>foo : Symbol(foo, Decl(circularAccessorAnnotations.ts, 8, 19), Decl(circularAccessorAnnotations.ts, 9, 22)) +} + +type T1 = { +>T1 : Symbol(T1, Decl(circularAccessorAnnotations.ts, 11, 1)) + + get foo(): T1["foo"]; +>foo : Symbol(foo, Decl(circularAccessorAnnotations.ts, 13, 11)) +>T1 : Symbol(T1, Decl(circularAccessorAnnotations.ts, 11, 1)) +} + +type T2 = { +>T2 : Symbol(T2, Decl(circularAccessorAnnotations.ts, 15, 1)) + + set foo(value: T2["foo"]); +>foo : Symbol(foo, Decl(circularAccessorAnnotations.ts, 17, 11)) +>value : Symbol(value, Decl(circularAccessorAnnotations.ts, 18, 12)) +>T2 : Symbol(T2, Decl(circularAccessorAnnotations.ts, 15, 1)) +} + +type T3 = { +>T3 : Symbol(T3, Decl(circularAccessorAnnotations.ts, 19, 1)) + + get foo(): string; +>foo : Symbol(foo, Decl(circularAccessorAnnotations.ts, 21, 11), Decl(circularAccessorAnnotations.ts, 22, 22)) + + set foo(value: T3["foo"]); +>foo : Symbol(foo, Decl(circularAccessorAnnotations.ts, 21, 11), Decl(circularAccessorAnnotations.ts, 22, 22)) +>value : Symbol(value, Decl(circularAccessorAnnotations.ts, 23, 12)) +>T3 : Symbol(T3, Decl(circularAccessorAnnotations.ts, 19, 1)) +} + diff --git a/tests/baselines/reference/circularAccessorAnnotations.types b/tests/baselines/reference/circularAccessorAnnotations.types new file mode 100644 index 0000000000000..1945fdc51092a --- /dev/null +++ b/tests/baselines/reference/circularAccessorAnnotations.types @@ -0,0 +1,62 @@ +=== tests/cases/compiler/circularAccessorAnnotations.ts === +declare const c1: { +>c1 : { readonly foo: any; } + + get foo(): typeof c1.foo; +>foo : any +>c1.foo : any +>c1 : { readonly foo: any; } +>foo : any +} + +declare const c2: { +>c2 : { foo: any; } + + set foo(value: typeof c2.foo); +>foo : any +>value : any +>c2.foo : any +>c2 : { foo: any; } +>foo : any +} + +declare const c3: { +>c3 : { foo: string; } + + get foo(): string; +>foo : string + + set foo(value: typeof c3.foo); +>foo : string +>value : string +>c3.foo : string +>c3 : { foo: string; } +>foo : string +} + +type T1 = { +>T1 : T1 + + get foo(): T1["foo"]; +>foo : any +} + +type T2 = { +>T2 : T2 + + set foo(value: T2["foo"]); +>foo : any +>value : any +} + +type T3 = { +>T3 : T3 + + get foo(): string; +>foo : string + + set foo(value: T3["foo"]); +>foo : string +>value : string +} + diff --git a/tests/baselines/reference/circularIndexedAccessErrors.errors.txt b/tests/baselines/reference/circularIndexedAccessErrors.errors.txt index f2bf858ab9e0e..3bd2d323f1cf4 100644 --- a/tests/baselines/reference/circularIndexedAccessErrors.errors.txt +++ b/tests/baselines/reference/circularIndexedAccessErrors.errors.txt @@ -1,5 +1,5 @@ tests/cases/conformance/types/keyof/circularIndexedAccessErrors.ts(2,5): error TS2502: 'x' is referenced directly or indirectly in its own type annotation. -tests/cases/conformance/types/keyof/circularIndexedAccessErrors.ts(6,5): error TS2502: 'x' is referenced directly or indirectly in its own type annotation. +tests/cases/conformance/types/keyof/circularIndexedAccessErrors.ts(11,11): error TS2589: Type instantiation is excessively deep and possibly infinite. tests/cases/conformance/types/keyof/circularIndexedAccessErrors.ts(18,5): error TS2502: 'x' is referenced directly or indirectly in its own type annotation. tests/cases/conformance/types/keyof/circularIndexedAccessErrors.ts(22,5): error TS2502: 'x' is referenced directly or indirectly in its own type annotation. tests/cases/conformance/types/keyof/circularIndexedAccessErrors.ts(37,24): error TS2313: Type parameter 'T' has a circular constraint. @@ -15,13 +15,13 @@ tests/cases/conformance/types/keyof/circularIndexedAccessErrors.ts(37,30): error type T2 = { x: T2[K]; // Error - ~ -!!! error TS2502: 'x' is referenced directly or indirectly in its own type annotation. y: number; } declare let x2: T2<"x">; let x2x = x2.x; + ~~~~ +!!! error TS2589: Type instantiation is excessively deep and possibly infinite. interface T3> { x: T["x"]; diff --git a/tests/baselines/reference/limitDeepInstantiations.errors.txt b/tests/baselines/reference/limitDeepInstantiations.errors.txt index 2cf5aade7fd61..21b5f53b04e13 100644 --- a/tests/baselines/reference/limitDeepInstantiations.errors.txt +++ b/tests/baselines/reference/limitDeepInstantiations.errors.txt @@ -1,4 +1,4 @@ -tests/cases/compiler/limitDeepInstantiations.ts(3,35): error TS2502: '"true"' is referenced directly or indirectly in its own type annotation. +tests/cases/compiler/limitDeepInstantiations.ts(4,9): error TS2589: Type instantiation is excessively deep and possibly infinite. tests/cases/compiler/limitDeepInstantiations.ts(5,13): error TS2344: Type '"false"' does not satisfy the constraint '"true"'. @@ -6,9 +6,9 @@ tests/cases/compiler/limitDeepInstantiations.ts(5,13): error TS2344: Type '"fals // Repro from #14837 type Foo = { "true": Foo> }[T]; - ~~~~~~ -!!! error TS2502: '"true"' is referenced directly or indirectly in its own type annotation. let f1: Foo<"true", {}>; + ~~~~~~~~~~~~~~~ +!!! error TS2589: Type instantiation is excessively deep and possibly infinite. let f2: Foo<"false", {}>; ~~~~~~~ !!! error TS2344: Type '"false"' does not satisfy the constraint '"true"'. diff --git a/tests/cases/compiler/circularAccessorAnnotations.ts b/tests/cases/compiler/circularAccessorAnnotations.ts new file mode 100644 index 0000000000000..0f6908bff817b --- /dev/null +++ b/tests/cases/compiler/circularAccessorAnnotations.ts @@ -0,0 +1,28 @@ +// @strict: true +// @declaration: true + +declare const c1: { + get foo(): typeof c1.foo; +} + +declare const c2: { + set foo(value: typeof c2.foo); +} + +declare const c3: { + get foo(): string; + set foo(value: typeof c3.foo); +} + +type T1 = { + get foo(): T1["foo"]; +} + +type T2 = { + set foo(value: T2["foo"]); +} + +type T3 = { + get foo(): string; + set foo(value: T3["foo"]); +}