Implement 'deferred' modifier, remove Deferred<T> marker type

This commit is contained in:
Anders Hejlsberg
2024-06-13 07:14:09 -07:00
parent 738f6b5087
commit 3ae7cb22dc
11 changed files with 50 additions and 39 deletions
+23 -27
View File
@@ -1396,7 +1396,6 @@ const enum IntrinsicTypeKind {
Capitalize,
Uncapitalize,
NoInfer,
Deferred,
}
const intrinsicTypeKinds: ReadonlyMap<string, IntrinsicTypeKind> = new Map(Object.entries({
@@ -1405,7 +1404,6 @@ const intrinsicTypeKinds: ReadonlyMap<string, IntrinsicTypeKind> = new Map(Objec
Capitalize: IntrinsicTypeKind.Capitalize,
Uncapitalize: IntrinsicTypeKind.Uncapitalize,
NoInfer: IntrinsicTypeKind.NoInfer,
Deferred: IntrinsicTypeKind.Deferred,
}));
const SymbolLinks = class implements SymbolLinks {
@@ -16105,9 +16103,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
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]) :
typeKind === IntrinsicTypeKind.Deferred ? getDeferredCallbackType(typeArguments[0], aliasSymbol, aliasTypeArguments) :
getStringMappingType(symbol, typeArguments[0]);
return typeKind === IntrinsicTypeKind.NoInfer ? getNoInferType(typeArguments[0]) : getStringMappingType(symbol, typeArguments[0]);
}
}
const links = getSymbolLinks(symbol);
@@ -16120,27 +16116,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return instantiation;
}
function getDeferredCallbackType(type: Type, aliasSymbol: Symbol | undefined, aliasTypeArguments: readonly Type[] | undefined) {
if (type.flags & TypeFlags.Object) {
const key = `F${getTypeId(type)}${getAliasId(aliasSymbol, aliasTypeArguments)}`;
return getCachedType(key) ?? setCachedType(key, createDeferredCallbackType(type as ObjectType, aliasSymbol, aliasTypeArguments));
}
return type;
}
function createDeferredCallbackType(type: ObjectType, aliasSymbol: Symbol | undefined, aliasTypeArguments: readonly Type[] | undefined) {
const resolved = resolveStructuredTypeMembers(type);
const result = createObjectType(ObjectFlags.Anonymous | ObjectFlags.DeferredCallback, type.symbol);
result.members = resolved.members;
result.properties = resolved.properties;
result.callSignatures = resolved.callSignatures;
result.constructSignatures = resolved.constructSignatures;
result.indexInfos = resolved.indexInfos;
result.aliasSymbol = aliasSymbol;
result.aliasTypeArguments = aliasTypeArguments;
return result;
}
/**
* Get type from reference to type alias. When a type alias is generic, the declared type of the type alias may include
* references to the type parameters of the alias. We replace those with the actual type arguments by instantiating the
@@ -28190,7 +28165,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
// circularities. Instead, we simply check if any signature has a deferred callback marker in the
// particular argument position.
signatures ??= getSignaturesOfType(getTypeOfExpression(flow.node.expression), SignatureKind.Call);
if (!some(signatures, sig => !!(getObjectFlags(getTypeAtPosition(sig, i)) & ObjectFlags.DeferredCallback))) {
if (!some(signatures, sig => !!(getModifiersAtPosition(sig, i) & ModifierFlags.Deferred))) {
const lambdaType = getTypeFromFlowType(getTypeAtFlowNode(lambda.returnFlowNode));
if (lambdaType !== initialType) {
lambdaTypes ??= [initialType];
@@ -37257,6 +37232,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return elementType && isTypeAny(elementType) ? anyType : restType;
}
function getModifiersAtPosition(signature: Signature, pos: number) {
const index = pos < signature.parameters.length ? pos : signatureHasRestParameter(signature) ? signature.parameters.length - 1 : -1;
return index >= 0 ? getDeclarationModifierFlagsFromSymbol(signature.parameters[index]) : ModifierFlags.None;
}
// Return the number of parameters in a signature. The rest parameter, if present, counts as one
// parameter. For example, the parameter count of (x: number, y: number, ...z: string[]) is 3 and
// the parameter count of (x: number, ...args: [number, ...string[], boolean])) is also 3. In the
@@ -40570,6 +40550,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
error(node.name, Diagnostics.constructor_cannot_be_used_as_a_parameter_property_name);
}
}
if (hasSyntacticModifier(node, ModifierFlags.Deferred)) {
const funcType = node.dotDotDotToken ? createArrayType(globalFunctionType, /*readonly*/ true) : globalFunctionType;
if (!isTypeAssignableTo(getTypeOfSymbol(node.symbol), funcType)) {
error(node, Diagnostics.A_deferred_parameter_must_have_a_function_type);
}
}
if (!node.initializer && isOptionalDeclaration(node) && isBindingPattern(node.name) && (func as FunctionLikeDeclaration).body) {
error(node, Diagnostics.A_binding_pattern_parameter_cannot_be_optional_in_an_implementation_signature);
}
@@ -50151,6 +50137,16 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
flags |= inOutFlag;
break;
}
case SyntaxKind.DeferredKeyword:
if (node.kind !== SyntaxKind.Parameter) {
return grammarErrorOnNode(modifier, Diagnostics.deferred_modifier_can_only_appear_on_a_parameter_declaration);
}
if (flags & ModifierFlags.Deferred) {
return grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "deferred");
}
flags |= ModifierFlags.Deferred;
break;
}
}
}
+8
View File
@@ -971,6 +971,10 @@
"category": "Error",
"code": 1293
},
"'deferred' modifier can only appear on a parameter declaration.": {
"category": "Error",
"code": 1294
},
"'with' statements are not allowed in an async function block.": {
"category": "Error",
@@ -3911,6 +3915,10 @@
"category": "Error",
"code": 2868
},
"A 'deferred' parameter must have a function type.": {
"category": "Error",
"code": 2869
},
"Import declaration '{0}' is using private name '{1}'.": {
"category": "Error",
+2
View File
@@ -1471,6 +1471,7 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode
case SyntaxKind.ReadonlyKeyword:
case SyntaxKind.AbstractKeyword:
case SyntaxKind.DeclareKeyword:
case SyntaxKind.DeferredKeyword:
case SyntaxKind.ConstKeyword:
case SyntaxKind.AnyKeyword:
case SyntaxKind.NumberKeyword:
@@ -1566,6 +1567,7 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode
if (flags & ModifierFlags.Async) result.push(createModifier(SyntaxKind.AsyncKeyword));
if (flags & ModifierFlags.In) result.push(createModifier(SyntaxKind.InKeyword));
if (flags & ModifierFlags.Out) result.push(createModifier(SyntaxKind.OutKeyword));
if (flags & ModifierFlags.Deferred) result.push(createModifier(SyntaxKind.DeferredKeyword));
return result.length ? result : undefined;
}
+1
View File
@@ -3322,6 +3322,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
case SyntaxKind.ProtectedKeyword:
case SyntaxKind.ReadonlyKeyword:
case SyntaxKind.DeclareKeyword:
case SyntaxKind.DeferredKeyword:
case SyntaxKind.AbstractKeyword:
case SyntaxKind.OverrideKeyword:
case SyntaxKind.InKeyword:
+1
View File
@@ -148,6 +148,7 @@ export const textToKeywordObj: MapLike<KeywordSyntaxKind> = {
debugger: SyntaxKind.DebuggerKeyword,
declare: SyntaxKind.DeclareKeyword,
default: SyntaxKind.DefaultKeyword,
deferred: SyntaxKind.DeferredKeyword,
delete: SyntaxKind.DeleteKeyword,
do: SyntaxKind.DoKeyword,
else: SyntaxKind.ElseKeyword,
+1
View File
@@ -661,6 +661,7 @@ export function transformTypeScript(context: TransformationContext) {
case SyntaxKind.OverrideKeyword:
case SyntaxKind.ConstKeyword:
case SyntaxKind.DeclareKeyword:
case SyntaxKind.DeferredKeyword:
case SyntaxKind.ReadonlyKeyword:
case SyntaxKind.InKeyword:
case SyntaxKind.OutKeyword:
+11 -6
View File
@@ -196,6 +196,7 @@ export const enum SyntaxKind {
BooleanKeyword,
ConstructorKeyword,
DeclareKeyword,
DeferredKeyword,
GetKeyword,
InferKeyword,
IntrinsicKeyword,
@@ -598,6 +599,7 @@ export type KeywordSyntaxKind =
| SyntaxKind.DebuggerKeyword
| SyntaxKind.DeclareKeyword
| SyntaxKind.DefaultKeyword
| SyntaxKind.DeferredKeyword
| SyntaxKind.DeleteKeyword
| SyntaxKind.DoKeyword
| SyntaxKind.ElseKeyword
@@ -669,6 +671,7 @@ export type ModifierSyntaxKind =
| SyntaxKind.ConstKeyword
| SyntaxKind.DeclareKeyword
| SyntaxKind.DefaultKeyword
| SyntaxKind.DeferredKeyword
| SyntaxKind.ExportKeyword
| SyntaxKind.InKeyword
| SyntaxKind.PrivateKeyword
@@ -865,10 +868,11 @@ export const enum ModifierFlags {
Const = 1 << 12, // Const enum
In = 1 << 13, // Contravariance modifier
Out = 1 << 14, // Covariance modifier
Decorator = 1 << 15, // Contains a decorator.
Deferred = 1 << 15, // Parameter
Decorator = 1 << 16, // Contains a decorator.
// JSDoc-only modifiers
Deprecated = 1 << 16, // Deprecated tag.
Deprecated = 1 << 17, // Deprecated tag.
// Cache-only JSDoc-modifiers. Should match order of Syntactic/JSDoc modifiers, above.
/** @internal */ JSDocPublic = 1 << 23, // if this value changes, `selectEffectiveModifierFlags` must change accordingly
@@ -878,7 +882,7 @@ export const enum ModifierFlags {
/** @internal */ JSDocOverride = 1 << 27,
/** @internal */ SyntacticOrJSDocModifiers = Public | Private | Protected | Readonly | Override,
/** @internal */ SyntacticOnlyModifiers = Export | Ambient | Abstract | Static | Accessor | Async | Default | Const | In | Out | Decorator,
/** @internal */ SyntacticOnlyModifiers = Export | Ambient | Abstract | Static | Accessor | Async | Default | Const | In | Out | Deferred | Decorator,
/** @internal */ SyntacticModifiers = SyntacticOrJSDocModifiers | SyntacticOnlyModifiers,
/** @internal */ JSDocCacheOnlyModifiers = JSDocPublic | JSDocPrivate | JSDocProtected | JSDocReadonly | JSDocOverride,
/** @internal */ JSDocOnlyModifiers = Deprecated,
@@ -892,9 +896,9 @@ export const enum ModifierFlags {
ParameterPropertyModifier = AccessibilityModifier | Readonly | Override,
NonPublicAccessibilityModifier = Private | Protected,
TypeScriptModifier = Ambient | Public | Private | Protected | Readonly | Abstract | Const | Override | In | Out,
TypeScriptModifier = Ambient | Public | Private | Protected | Readonly | Abstract | Const | Override | In | Out | Deferred,
ExportDefault = Export | Default,
All = Export | Ambient | Public | Private | Protected | Static | Readonly | Abstract | Accessor | Async | Default | Const | Deprecated | Override | In | Out | Decorator,
All = Export | Ambient | Public | Private | Protected | Static | Readonly | Abstract | Accessor | Async | Default | Const | Deprecated | Override | In | Out | Deferred | Decorator,
Modifier = All & ~Decorator,
}
@@ -1620,6 +1624,7 @@ export type AsyncKeyword = ModifierToken<SyntaxKind.AsyncKeyword>;
export type ConstKeyword = ModifierToken<SyntaxKind.ConstKeyword>;
export type DeclareKeyword = ModifierToken<SyntaxKind.DeclareKeyword>;
export type DefaultKeyword = ModifierToken<SyntaxKind.DefaultKeyword>;
export type DeferredKeyword = ModifierToken<SyntaxKind.DeferredKeyword>;
export type ExportKeyword = ModifierToken<SyntaxKind.ExportKeyword>;
export type InKeyword = ModifierToken<SyntaxKind.InKeyword>;
export type PrivateKeyword = ModifierToken<SyntaxKind.PrivateKeyword>;
@@ -1637,6 +1642,7 @@ export type Modifier =
| ConstKeyword
| DeclareKeyword
| DefaultKeyword
| DeferredKeyword
| ExportKeyword
| InKeyword
| PrivateKeyword
@@ -6389,7 +6395,6 @@ export const enum ObjectFlags {
/** @internal */
IdenticalBaseTypeExists = 1 << 26, // has a defined cachedEquivalentBaseType member
SingleSignatureType = 1 << 27, // A single signature type extracted from a potentially broader type
DeferredCallback = 1 << 28, // Function type with Deferred<T> marker
// Flags that require TypeFlags.UnionOrIntersection or TypeFlags.Substitution
/** @internal */
+2
View File
@@ -7159,6 +7159,8 @@ export function modifierToFlag(token: SyntaxKind): ModifierFlags {
return ModifierFlags.In;
case SyntaxKind.OutKeyword:
return ModifierFlags.Out;
case SyntaxKind.DeferredKeyword:
return ModifierFlags.Deferred;
case SyntaxKind.Decorator:
return ModifierFlags.Decorator;
}
+1
View File
@@ -1568,6 +1568,7 @@ export function isModifierKind(token: SyntaxKind): token is Modifier["kind"] {
case SyntaxKind.ConstKeyword:
case SyntaxKind.DeclareKeyword:
case SyntaxKind.DefaultKeyword:
case SyntaxKind.DeferredKeyword:
case SyntaxKind.ExportKeyword:
case SyntaxKind.InKeyword:
case SyntaxKind.PublicKeyword:
-1
View File
@@ -1210,7 +1210,6 @@ export namespace Completion {
typeEntry("Capitalize"),
typeEntry("Uncapitalize"),
typeEntry("NoInfer"),
typeEntry("Deferred"),
interfaceEntry("ThisType"),
varEntry("ArrayBuffer"),
interfaceEntry("ArrayBufferTypes"),
-5
View File
@@ -1654,11 +1654,6 @@ type Uncapitalize<S extends string> = intrinsic;
*/
type NoInfer<T> = intrinsic;
/**
* Marker for deferred callbacks
*/
type Deferred<T extends (...args: any) => any> = intrinsic;
/**
* Marker for contextual 'this' type
*/