mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-04-12 23:36:28 -05:00
Fix lint issues and finalize implementation
Co-authored-by: RyanCavanaugh <6685088+RyanCavanaugh@users.noreply.github.com>
This commit is contained in:
@@ -17542,51 +17542,51 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
return (deferredGlobalIteratorReturnResultType ||= getGlobalType("IteratorReturnResult" as __String, /*arity*/ 1, reportErrors)) || emptyGenericType;
|
||||
}
|
||||
|
||||
function getGlobalDisposableType(reportErrors: boolean) {
|
||||
function _getGlobalDisposableType(reportErrors: boolean) {
|
||||
return (deferredGlobalDisposableType ||= getGlobalType("Disposable" as __String, /*arity*/ 0, reportErrors)) || emptyObjectType;
|
||||
}
|
||||
|
||||
function getGlobalAsyncDisposableType(reportErrors: boolean) {
|
||||
return (deferredGlobalAsyncDisposableType ||= getGlobalType("AsyncDisposable" as __String, /*arity*/ 0, reportErrors)) || emptyObjectType;
|
||||
}
|
||||
|
||||
function checkTypeIsDisposable(type: Type): boolean {
|
||||
if (type.flags & (TypeFlags.Null | TypeFlags.Undefined)) {
|
||||
return true; // null and undefined are allowed
|
||||
}
|
||||
|
||||
// Handle union types
|
||||
if (type.flags & TypeFlags.Union) {
|
||||
return every((type as UnionType).types, checkTypeIsDisposable);
|
||||
}
|
||||
|
||||
const disposePropertyName = getPropertyNameForKnownSymbolName("dispose");
|
||||
const disposeProperty = getPropertyOfType(type, disposePropertyName);
|
||||
|
||||
return !!disposeProperty && !(disposeProperty.flags & SymbolFlags.Optional);
|
||||
}
|
||||
|
||||
function checkTypeIsAsyncDisposable(type: Type): boolean {
|
||||
if (type.flags & (TypeFlags.Null | TypeFlags.Undefined)) {
|
||||
return true; // null and undefined are allowed
|
||||
}
|
||||
|
||||
// Handle union types
|
||||
if (type.flags & TypeFlags.Union) {
|
||||
return every((type as UnionType).types, checkTypeIsAsyncDisposable);
|
||||
}
|
||||
|
||||
const asyncDisposePropertyName = getPropertyNameForKnownSymbolName("asyncDispose");
|
||||
const asyncDisposeProperty = getPropertyOfType(type, asyncDisposePropertyName);
|
||||
|
||||
if (asyncDisposeProperty && !(asyncDisposeProperty.flags & SymbolFlags.Optional)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// For await using, also check for Symbol.dispose as a fallback
|
||||
return checkTypeIsDisposable(type);
|
||||
}
|
||||
|
||||
function _getGlobalAsyncDisposableType(reportErrors: boolean) {
|
||||
return (deferredGlobalAsyncDisposableType ||= getGlobalType("AsyncDisposable" as __String, /*arity*/ 0, reportErrors)) || emptyObjectType;
|
||||
}
|
||||
|
||||
function checkTypeIsDisposable(type: Type): boolean {
|
||||
if (type.flags & (TypeFlags.Null | TypeFlags.Undefined)) {
|
||||
return true; // null and undefined are allowed
|
||||
}
|
||||
|
||||
// Handle union types
|
||||
if (type.flags & TypeFlags.Union) {
|
||||
return every((type as UnionType).types, checkTypeIsDisposable);
|
||||
}
|
||||
|
||||
const disposePropertyName = getPropertyNameForKnownSymbolName("dispose");
|
||||
const disposeProperty = getPropertyOfType(type, disposePropertyName);
|
||||
|
||||
return !!disposeProperty && !(disposeProperty.flags & SymbolFlags.Optional);
|
||||
}
|
||||
|
||||
function checkTypeIsAsyncDisposable(type: Type): boolean {
|
||||
if (type.flags & (TypeFlags.Null | TypeFlags.Undefined)) {
|
||||
return true; // null and undefined are allowed
|
||||
}
|
||||
|
||||
// Handle union types
|
||||
if (type.flags & TypeFlags.Union) {
|
||||
return every((type as UnionType).types, checkTypeIsAsyncDisposable);
|
||||
}
|
||||
|
||||
const asyncDisposePropertyName = getPropertyNameForKnownSymbolName("asyncDispose");
|
||||
const asyncDisposeProperty = getPropertyOfType(type, asyncDisposePropertyName);
|
||||
|
||||
if (asyncDisposeProperty && !(asyncDisposeProperty.flags & SymbolFlags.Optional)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// For await using, also check for Symbol.dispose as a fallback
|
||||
return checkTypeIsDisposable(type);
|
||||
}
|
||||
|
||||
function getGlobalTypeOrUndefined(name: __String, arity = 0): ObjectType | undefined {
|
||||
const symbol = getGlobalSymbol(name, SymbolFlags.Type, /*diagnostic*/ undefined);
|
||||
return symbol && getTypeOfGlobalSymbol(symbol, arity) as GenericType;
|
||||
@@ -45034,16 +45034,16 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
if (!isJSObjectLiteralInitializer && node.parent.parent.kind !== SyntaxKind.ForInStatement) {
|
||||
const initializerType = checkExpressionCached(initializer);
|
||||
checkTypeAssignableToAndOptionallyElaborate(initializerType, type, node, initializer, /*headMessage*/ undefined);
|
||||
const blockScopeKind = getCombinedNodeFlagsCached(node) & NodeFlags.BlockScoped;
|
||||
if (blockScopeKind === NodeFlags.AwaitUsing) {
|
||||
if (!checkTypeIsAsyncDisposable(widenTypeForVariableLikeDeclaration(initializerType, node))) {
|
||||
error(initializer, Diagnostics.The_initializer_of_an_await_using_declaration_must_be_either_an_object_with_a_Symbol_asyncDispose_or_Symbol_dispose_method_or_be_null_or_undefined);
|
||||
}
|
||||
}
|
||||
else if (blockScopeKind === NodeFlags.Using) {
|
||||
if (!checkTypeIsDisposable(widenTypeForVariableLikeDeclaration(initializerType, node))) {
|
||||
error(initializer, Diagnostics.The_initializer_of_a_using_declaration_must_be_either_an_object_with_a_Symbol_dispose_method_or_be_null_or_undefined);
|
||||
}
|
||||
const blockScopeKind = getCombinedNodeFlagsCached(node) & NodeFlags.BlockScoped;
|
||||
if (blockScopeKind === NodeFlags.AwaitUsing) {
|
||||
if (!checkTypeIsAsyncDisposable(initializerType)) {
|
||||
error(initializer, Diagnostics.The_initializer_of_an_await_using_declaration_must_be_either_an_object_with_a_Symbol_asyncDispose_or_Symbol_dispose_method_or_be_null_or_undefined);
|
||||
}
|
||||
}
|
||||
else if (blockScopeKind === NodeFlags.Using) {
|
||||
if (!checkTypeIsDisposable(initializerType)) {
|
||||
error(initializer, Diagnostics.The_initializer_of_a_using_declaration_must_be_either_an_object_with_a_Symbol_dispose_method_or_be_null_or_undefined);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
usingDeclarationWithGlobalInterfaceModification.ts(26,17): error TS2850: The initializer of a 'using' declaration must be either an object with a '[Symbol.dispose]()' method, or be 'null' or 'undefined'.
|
||||
|
||||
|
||||
==== usingDeclarationWithGlobalInterfaceModification.ts (1 errors) ====
|
||||
// Test case that demonstrates the issue from https://github.com/microsoft/TypeScript/issues/62121
|
||||
// When an empty global Disposable interface is declared, it should NOT affect
|
||||
// the checking for Symbol.dispose properties
|
||||
|
||||
declare global {
|
||||
interface Disposable {}
|
||||
}
|
||||
|
||||
// This should pass - has Symbol.dispose method
|
||||
const validDisposable = {
|
||||
[Symbol.dispose]() {
|
||||
// disposed
|
||||
}
|
||||
};
|
||||
|
||||
// This should fail - no Symbol.dispose method
|
||||
const invalidDisposable = {
|
||||
cleanup() {
|
||||
// cleanup
|
||||
}
|
||||
};
|
||||
|
||||
// With the fix, the checker should directly check for Symbol.dispose properties
|
||||
// rather than relying on assignability to the global Disposable interface
|
||||
using valid = validDisposable; // should pass
|
||||
using invalid = invalidDisposable; // should error
|
||||
~~~~~~~~~~~~~~~~~
|
||||
!!! error TS2850: The initializer of a 'using' declaration must be either an object with a '[Symbol.dispose]()' method, or be 'null' or 'undefined'.
|
||||
|
||||
export {};
|
||||
@@ -0,0 +1,53 @@
|
||||
//// [tests/cases/compiler/usingDeclarationWithGlobalInterfaceModification.ts] ////
|
||||
|
||||
//// [usingDeclarationWithGlobalInterfaceModification.ts]
|
||||
// Test case that demonstrates the issue from https://github.com/microsoft/TypeScript/issues/62121
|
||||
// When an empty global Disposable interface is declared, it should NOT affect
|
||||
// the checking for Symbol.dispose properties
|
||||
|
||||
declare global {
|
||||
interface Disposable {}
|
||||
}
|
||||
|
||||
// This should pass - has Symbol.dispose method
|
||||
const validDisposable = {
|
||||
[Symbol.dispose]() {
|
||||
// disposed
|
||||
}
|
||||
};
|
||||
|
||||
// This should fail - no Symbol.dispose method
|
||||
const invalidDisposable = {
|
||||
cleanup() {
|
||||
// cleanup
|
||||
}
|
||||
};
|
||||
|
||||
// With the fix, the checker should directly check for Symbol.dispose properties
|
||||
// rather than relying on assignability to the global Disposable interface
|
||||
using valid = validDisposable; // should pass
|
||||
using invalid = invalidDisposable; // should error
|
||||
|
||||
export {};
|
||||
|
||||
//// [usingDeclarationWithGlobalInterfaceModification.js]
|
||||
// Test case that demonstrates the issue from https://github.com/microsoft/TypeScript/issues/62121
|
||||
// When an empty global Disposable interface is declared, it should NOT affect
|
||||
// the checking for Symbol.dispose properties
|
||||
// This should pass - has Symbol.dispose method
|
||||
const validDisposable = {
|
||||
[Symbol.dispose]() {
|
||||
// disposed
|
||||
}
|
||||
};
|
||||
// This should fail - no Symbol.dispose method
|
||||
const invalidDisposable = {
|
||||
cleanup() {
|
||||
// cleanup
|
||||
}
|
||||
};
|
||||
// With the fix, the checker should directly check for Symbol.dispose properties
|
||||
// rather than relying on assignability to the global Disposable interface
|
||||
using valid = validDisposable; // should pass
|
||||
using invalid = invalidDisposable; // should error
|
||||
export {};
|
||||
@@ -0,0 +1,50 @@
|
||||
//// [tests/cases/compiler/usingDeclarationWithGlobalInterfaceModification.ts] ////
|
||||
|
||||
=== usingDeclarationWithGlobalInterfaceModification.ts ===
|
||||
// Test case that demonstrates the issue from https://github.com/microsoft/TypeScript/issues/62121
|
||||
// When an empty global Disposable interface is declared, it should NOT affect
|
||||
// the checking for Symbol.dispose properties
|
||||
|
||||
declare global {
|
||||
>global : Symbol(global, Decl(usingDeclarationWithGlobalInterfaceModification.ts, 0, 0))
|
||||
|
||||
interface Disposable {}
|
||||
>Disposable : Symbol(Disposable, Decl(lib.esnext.disposable.d.ts, --, --), Decl(usingDeclarationWithGlobalInterfaceModification.ts, 4, 16))
|
||||
}
|
||||
|
||||
// This should pass - has Symbol.dispose method
|
||||
const validDisposable = {
|
||||
>validDisposable : Symbol(validDisposable, Decl(usingDeclarationWithGlobalInterfaceModification.ts, 9, 5))
|
||||
|
||||
[Symbol.dispose]() {
|
||||
>[Symbol.dispose] : Symbol([Symbol.dispose], Decl(usingDeclarationWithGlobalInterfaceModification.ts, 9, 25))
|
||||
>Symbol.dispose : Symbol(SymbolConstructor.dispose, Decl(lib.esnext.disposable.d.ts, --, --))
|
||||
>Symbol : Symbol(Symbol, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.symbol.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2019.symbol.d.ts, --, --))
|
||||
>dispose : Symbol(SymbolConstructor.dispose, Decl(lib.esnext.disposable.d.ts, --, --))
|
||||
|
||||
// disposed
|
||||
}
|
||||
};
|
||||
|
||||
// This should fail - no Symbol.dispose method
|
||||
const invalidDisposable = {
|
||||
>invalidDisposable : Symbol(invalidDisposable, Decl(usingDeclarationWithGlobalInterfaceModification.ts, 16, 5))
|
||||
|
||||
cleanup() {
|
||||
>cleanup : Symbol(cleanup, Decl(usingDeclarationWithGlobalInterfaceModification.ts, 16, 27))
|
||||
|
||||
// cleanup
|
||||
}
|
||||
};
|
||||
|
||||
// With the fix, the checker should directly check for Symbol.dispose properties
|
||||
// rather than relying on assignability to the global Disposable interface
|
||||
using valid = validDisposable; // should pass
|
||||
>valid : Symbol(valid, Decl(usingDeclarationWithGlobalInterfaceModification.ts, 24, 5))
|
||||
>validDisposable : Symbol(validDisposable, Decl(usingDeclarationWithGlobalInterfaceModification.ts, 9, 5))
|
||||
|
||||
using invalid = invalidDisposable; // should error
|
||||
>invalid : Symbol(invalid, Decl(usingDeclarationWithGlobalInterfaceModification.ts, 25, 5))
|
||||
>invalidDisposable : Symbol(invalidDisposable, Decl(usingDeclarationWithGlobalInterfaceModification.ts, 16, 5))
|
||||
|
||||
export {};
|
||||
@@ -0,0 +1,65 @@
|
||||
//// [tests/cases/compiler/usingDeclarationWithGlobalInterfaceModification.ts] ////
|
||||
|
||||
=== usingDeclarationWithGlobalInterfaceModification.ts ===
|
||||
// Test case that demonstrates the issue from https://github.com/microsoft/TypeScript/issues/62121
|
||||
// When an empty global Disposable interface is declared, it should NOT affect
|
||||
// the checking for Symbol.dispose properties
|
||||
|
||||
declare global {
|
||||
>global : any
|
||||
> : ^^^
|
||||
|
||||
interface Disposable {}
|
||||
}
|
||||
|
||||
// This should pass - has Symbol.dispose method
|
||||
const validDisposable = {
|
||||
>validDisposable : { [Symbol.dispose](): void; }
|
||||
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
>{ [Symbol.dispose]() { // disposed }} : { [Symbol.dispose](): void; }
|
||||
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
[Symbol.dispose]() {
|
||||
>[Symbol.dispose] : () => void
|
||||
> : ^^^^^^^^^^
|
||||
>Symbol.dispose : unique symbol
|
||||
> : ^^^^^^^^^^^^^
|
||||
>Symbol : SymbolConstructor
|
||||
> : ^^^^^^^^^^^^^^^^^
|
||||
>dispose : unique symbol
|
||||
> : ^^^^^^^^^^^^^
|
||||
|
||||
// disposed
|
||||
}
|
||||
};
|
||||
|
||||
// This should fail - no Symbol.dispose method
|
||||
const invalidDisposable = {
|
||||
>invalidDisposable : { cleanup(): void; }
|
||||
> : ^^^^^^^^^^^^^^^^^^^^
|
||||
>{ cleanup() { // cleanup }} : { cleanup(): void; }
|
||||
> : ^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
cleanup() {
|
||||
>cleanup : () => void
|
||||
> : ^^^^^^^^^^
|
||||
|
||||
// cleanup
|
||||
}
|
||||
};
|
||||
|
||||
// With the fix, the checker should directly check for Symbol.dispose properties
|
||||
// rather than relying on assignability to the global Disposable interface
|
||||
using valid = validDisposable; // should pass
|
||||
>valid : { [Symbol.dispose](): void; }
|
||||
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
>validDisposable : { [Symbol.dispose](): void; }
|
||||
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
using invalid = invalidDisposable; // should error
|
||||
>invalid : { cleanup(): void; }
|
||||
> : ^^^^^^^^^^^^^^^^^^^^
|
||||
>invalidDisposable : { cleanup(): void; }
|
||||
> : ^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
export {};
|
||||
Reference in New Issue
Block a user