mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Rework entity name decorator metadata fallback emit to not throw at runtime (#25421)
* Rework entity name decorator metadata fallback emit to not throw at runtime * Remove trailing whitespace
This commit is contained in:
@@ -1982,18 +1982,16 @@ namespace ts {
|
||||
const kind = resolver.getTypeReferenceSerializationKind(node.typeName, currentNameScope || currentLexicalScope);
|
||||
switch (kind) {
|
||||
case TypeReferenceSerializationKind.Unknown:
|
||||
const serialized = serializeEntityNameAsExpression(node.typeName, /*useFallback*/ true);
|
||||
const serialized = serializeEntityNameAsExpressionFallback(node.typeName);
|
||||
const temp = createTempVariable(hoistVariableDeclaration);
|
||||
return createLogicalOr(
|
||||
createLogicalAnd(
|
||||
createTypeCheck(createAssignment(temp, serialized), "function"),
|
||||
temp
|
||||
),
|
||||
return createConditional(
|
||||
createTypeCheck(createAssignment(temp, serialized), "function"),
|
||||
temp,
|
||||
createIdentifier("Object")
|
||||
);
|
||||
|
||||
case TypeReferenceSerializationKind.TypeWithConstructSignatureAndValue:
|
||||
return serializeEntityNameAsExpression(node.typeName, /*useFallback*/ false);
|
||||
return serializeEntityNameAsExpression(node.typeName);
|
||||
|
||||
case TypeReferenceSerializationKind.VoidNullableOrNeverType:
|
||||
return createVoidZero();
|
||||
@@ -2028,14 +2026,46 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function createCheckedValue(left: Expression, right: Expression) {
|
||||
return createLogicalAnd(
|
||||
createStrictInequality(createTypeOf(left), createLiteral("undefined")),
|
||||
right
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes an entity name which may not exist at runtime, but whose access shouldn't throw
|
||||
*
|
||||
* @param node The entity name to serialize.
|
||||
*/
|
||||
function serializeEntityNameAsExpressionFallback(node: EntityName): BinaryExpression {
|
||||
if (node.kind === SyntaxKind.Identifier) {
|
||||
// A -> typeof A !== undefined && A
|
||||
const copied = serializeEntityNameAsExpression(node);
|
||||
return createCheckedValue(copied, copied);
|
||||
}
|
||||
if (node.left.kind === SyntaxKind.Identifier) {
|
||||
// A.B -> typeof A !== undefined && A.B
|
||||
return createCheckedValue(serializeEntityNameAsExpression(node.left), serializeEntityNameAsExpression(node));
|
||||
}
|
||||
// A.B.C -> typeof A !== undefined && (_a = A.B) !== void 0 && _a.C
|
||||
const left = serializeEntityNameAsExpressionFallback(node.left);
|
||||
const temp = createTempVariable(hoistVariableDeclaration);
|
||||
return createLogicalAnd(
|
||||
createLogicalAnd(
|
||||
left.left,
|
||||
createStrictInequality(createAssignment(temp, left.right), createVoidZero())
|
||||
),
|
||||
createPropertyAccess(temp, node.right)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes an entity name as an expression for decorator type metadata.
|
||||
*
|
||||
* @param node The entity name to serialize.
|
||||
* @param useFallback A value indicating whether to use logical operators to test for the
|
||||
* entity name at runtime.
|
||||
*/
|
||||
function serializeEntityNameAsExpression(node: EntityName, useFallback: boolean): SerializedEntityNameAsExpression {
|
||||
function serializeEntityNameAsExpression(node: EntityName): SerializedEntityNameAsExpression {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.Identifier:
|
||||
// Create a clone of the name with a new parent, and treat it as if it were
|
||||
@@ -2044,20 +2074,11 @@ namespace ts {
|
||||
name.flags &= ~NodeFlags.Synthesized;
|
||||
name.original = undefined;
|
||||
name.parent = getParseTreeNode(currentLexicalScope); // ensure the parent is set to a parse tree node.
|
||||
if (useFallback) {
|
||||
return createLogicalAnd(
|
||||
createStrictInequality(
|
||||
createTypeOf(name),
|
||||
createLiteral("undefined")
|
||||
),
|
||||
name
|
||||
);
|
||||
}
|
||||
|
||||
return name;
|
||||
|
||||
case SyntaxKind.QualifiedName:
|
||||
return serializeQualifiedNameAsExpression(node, useFallback);
|
||||
return serializeQualifiedNameAsExpression(node);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2068,26 +2089,8 @@ namespace ts {
|
||||
* @param useFallback A value indicating whether to use logical operators to test for the
|
||||
* qualified name at runtime.
|
||||
*/
|
||||
function serializeQualifiedNameAsExpression(node: QualifiedName, useFallback: boolean): PropertyAccessExpression {
|
||||
let left: SerializedEntityNameAsExpression;
|
||||
if (node.left.kind === SyntaxKind.Identifier) {
|
||||
left = serializeEntityNameAsExpression(node.left, useFallback);
|
||||
}
|
||||
else if (useFallback) {
|
||||
const temp = createTempVariable(hoistVariableDeclaration);
|
||||
left = createLogicalAnd(
|
||||
createAssignment(
|
||||
temp,
|
||||
serializeEntityNameAsExpression(node.left, /*useFallback*/ true)
|
||||
),
|
||||
temp
|
||||
);
|
||||
}
|
||||
else {
|
||||
left = serializeEntityNameAsExpression(node.left, /*useFallback*/ false);
|
||||
}
|
||||
|
||||
return createPropertyAccess(left, node.right);
|
||||
function serializeQualifiedNameAsExpression(node: QualifiedName): SerializedEntityNameAsExpression {
|
||||
return createPropertyAccess(serializeEntityNameAsExpression(node.left), node.right);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -23,7 +23,7 @@ var B = /** @class */ (function () {
|
||||
var _a;
|
||||
__decorate([
|
||||
Decorate,
|
||||
__metadata("design:type", typeof (_a = typeof Map !== "undefined" && Map) === "function" && _a || Object)
|
||||
__metadata("design:type", typeof (_a = typeof Map !== "undefined" && Map) === "function" ? _a : Object)
|
||||
], B.prototype, "member");
|
||||
return B;
|
||||
}());
|
||||
|
||||
@@ -49,7 +49,7 @@ var MyClass = /** @class */ (function () {
|
||||
var _a;
|
||||
MyClass = __decorate([
|
||||
someDecorator,
|
||||
__metadata("design:paramtypes", [typeof (_a = (typeof db_1.default !== "undefined" && db_1.default).db) === "function" && _a || Object])
|
||||
__metadata("design:paramtypes", [typeof (_a = typeof db_1.default !== "undefined" && db_1.default.db) === "function" ? _a : Object])
|
||||
], MyClass);
|
||||
return MyClass;
|
||||
}());
|
||||
|
||||
@@ -49,7 +49,7 @@ var MyClass = /** @class */ (function () {
|
||||
var _a;
|
||||
MyClass = __decorate([
|
||||
someDecorator,
|
||||
__metadata("design:paramtypes", [typeof (_a = (typeof db_1.default !== "undefined" && db_1.default).db) === "function" && _a || Object])
|
||||
__metadata("design:paramtypes", [typeof (_a = typeof db_1.default !== "undefined" && db_1.default.db) === "function" ? _a : Object])
|
||||
], MyClass);
|
||||
return MyClass;
|
||||
}());
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
tests/cases/compiler/usage.ts(2,8): error TS2304: Cannot find name 'decorate'.
|
||||
tests/cases/compiler/usage.ts(2,31): error TS2694: Namespace 'A.B.C.D' has no exported member 'E'.
|
||||
|
||||
|
||||
==== tests/cases/compiler/types.d.ts (0 errors) ====
|
||||
declare namespace A {
|
||||
export namespace B {
|
||||
export namespace C {
|
||||
export namespace D {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
==== tests/cases/compiler/usage.ts (2 errors) ====
|
||||
class Foo {
|
||||
f(@decorate user: A.B.C.D.E): void {}
|
||||
~~~~~~~~
|
||||
!!! error TS2304: Cannot find name 'decorate'.
|
||||
~
|
||||
!!! error TS2694: Namespace 'A.B.C.D' has no exported member 'E'.
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
//// [tests/cases/compiler/experimentalDecoratorMetadataUnresolvedTypeObjectInEmit.ts] ////
|
||||
|
||||
//// [types.d.ts]
|
||||
declare namespace A {
|
||||
export namespace B {
|
||||
export namespace C {
|
||||
export namespace D {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//// [usage.ts]
|
||||
class Foo {
|
||||
f(@decorate user: A.B.C.D.E): void {}
|
||||
}
|
||||
|
||||
|
||||
//// [usage.js]
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __metadata = (this && this.__metadata) || function (k, v) {
|
||||
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
||||
};
|
||||
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
||||
return function (target, key) { decorator(target, key, paramIndex); }
|
||||
};
|
||||
var Foo = /** @class */ (function () {
|
||||
function Foo() {
|
||||
}
|
||||
Foo.prototype.f = function (user) { };
|
||||
var _a, _b, _c, _d;
|
||||
__decorate([
|
||||
__param(0, decorate),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", [typeof (_d = typeof A !== "undefined" && (_a = A.B) !== void 0 && (_b = _a.C) !== void 0 && (_c = _b.D) !== void 0 && _c.E) === "function" ? _d : Object]),
|
||||
__metadata("design:returntype", void 0)
|
||||
], Foo.prototype, "f");
|
||||
return Foo;
|
||||
}());
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
=== tests/cases/compiler/types.d.ts ===
|
||||
declare namespace A {
|
||||
>A : Symbol(A, Decl(types.d.ts, 0, 0))
|
||||
|
||||
export namespace B {
|
||||
>B : Symbol(B, Decl(types.d.ts, 0, 21))
|
||||
|
||||
export namespace C {
|
||||
>C : Symbol(C, Decl(types.d.ts, 1, 24))
|
||||
|
||||
export namespace D {
|
||||
>D : Symbol(D, Decl(types.d.ts, 2, 28))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
=== tests/cases/compiler/usage.ts ===
|
||||
class Foo {
|
||||
>Foo : Symbol(Foo, Decl(usage.ts, 0, 0))
|
||||
|
||||
f(@decorate user: A.B.C.D.E): void {}
|
||||
>f : Symbol(Foo.f, Decl(usage.ts, 0, 11))
|
||||
>user : Symbol(user, Decl(usage.ts, 1, 6))
|
||||
>A : Symbol(A, Decl(types.d.ts, 0, 0))
|
||||
>B : Symbol(A.B, Decl(types.d.ts, 0, 21))
|
||||
>C : Symbol(A.B.C, Decl(types.d.ts, 1, 24))
|
||||
>D : Symbol(A.B.C.D, Decl(types.d.ts, 2, 28))
|
||||
}
|
||||
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
=== tests/cases/compiler/types.d.ts ===
|
||||
declare namespace A {
|
||||
>A : any
|
||||
|
||||
export namespace B {
|
||||
>B : any
|
||||
|
||||
export namespace C {
|
||||
>C : any
|
||||
|
||||
export namespace D {
|
||||
>D : any
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
=== tests/cases/compiler/usage.ts ===
|
||||
class Foo {
|
||||
>Foo : Foo
|
||||
|
||||
f(@decorate user: A.B.C.D.E): void {}
|
||||
>f : (user: any) => void
|
||||
>decorate : any
|
||||
>user : any
|
||||
>A : any
|
||||
>B : any
|
||||
>C : any
|
||||
>D : any
|
||||
>E : No type information available!
|
||||
}
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ define(["require", "exports", "angular2/core"], function (require, exports, ng)
|
||||
var _a;
|
||||
MyClass1 = __decorate([
|
||||
foo,
|
||||
__metadata("design:paramtypes", [typeof (_a = (typeof ng !== "undefined" && ng).ElementRef) === "function" && _a || Object])
|
||||
__metadata("design:paramtypes", [typeof (_a = typeof ng !== "undefined" && ng.ElementRef) === "function" ? _a : Object])
|
||||
], MyClass1);
|
||||
return MyClass1;
|
||||
}());
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ var MyClass1 = /** @class */ (function () {
|
||||
var _a;
|
||||
MyClass1 = __decorate([
|
||||
foo,
|
||||
__metadata("design:paramtypes", [typeof (_a = (typeof ng !== "undefined" && ng).ElementRef) === "function" && _a || Object])
|
||||
__metadata("design:paramtypes", [typeof (_a = typeof ng !== "undefined" && ng.ElementRef) === "function" ? _a : Object])
|
||||
], MyClass1);
|
||||
return MyClass1;
|
||||
}());
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ define(["require", "exports", "angular2/core"], function (require, exports, ng)
|
||||
var _a;
|
||||
MyClass1 = __decorate([
|
||||
foo,
|
||||
__metadata("design:paramtypes", [typeof (_a = (typeof ng !== "undefined" && ng).ElementRef) === "function" && _a || Object])
|
||||
__metadata("design:paramtypes", [typeof (_a = typeof ng !== "undefined" && ng.ElementRef) === "function" ? _a : Object])
|
||||
], MyClass1);
|
||||
return MyClass1;
|
||||
}());
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ var MyClass1 = /** @class */ (function () {
|
||||
var _a;
|
||||
MyClass1 = __decorate([
|
||||
foo,
|
||||
__metadata("design:paramtypes", [typeof (_a = (typeof ng !== "undefined" && ng).ElementRef) === "function" && _a || Object])
|
||||
__metadata("design:paramtypes", [typeof (_a = typeof ng !== "undefined" && ng.ElementRef) === "function" ? _a : Object])
|
||||
], MyClass1);
|
||||
return MyClass1;
|
||||
}());
|
||||
|
||||
@@ -17,7 +17,7 @@ define(["require", "exports", "angular2/core"], function (require, exports, ng)
|
||||
var _a;
|
||||
MyClass1 = __decorate([
|
||||
foo,
|
||||
__metadata("design:paramtypes", [typeof (_a = (typeof ng !== "undefined" && ng).ElementRef) === "function" && _a || Object])
|
||||
__metadata("design:paramtypes", [typeof (_a = typeof ng !== "undefined" && ng.ElementRef) === "function" ? _a : Object])
|
||||
], MyClass1);
|
||||
return MyClass1;
|
||||
}());
|
||||
|
||||
@@ -17,7 +17,7 @@ var MyClass1 = /** @class */ (function () {
|
||||
var _a;
|
||||
MyClass1 = __decorate([
|
||||
foo,
|
||||
__metadata("design:paramtypes", [typeof (_a = (typeof ng !== "undefined" && ng).ElementRef) === "function" && _a || Object])
|
||||
__metadata("design:paramtypes", [typeof (_a = typeof ng !== "undefined" && ng.ElementRef) === "function" ? _a : Object])
|
||||
], MyClass1);
|
||||
return MyClass1;
|
||||
}());
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ define(["require", "exports", "angular2/core"], function (require, exports, ng)
|
||||
var _a;
|
||||
MyClass1 = __decorate([
|
||||
foo,
|
||||
__metadata("design:paramtypes", [typeof (_a = (typeof ng !== "undefined" && ng).ElementRef) === "function" && _a || Object])
|
||||
__metadata("design:paramtypes", [typeof (_a = typeof ng !== "undefined" && ng.ElementRef) === "function" ? _a : Object])
|
||||
], MyClass1);
|
||||
return MyClass1;
|
||||
}());
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ var MyClass1 = /** @class */ (function () {
|
||||
var _a;
|
||||
MyClass1 = __decorate([
|
||||
foo,
|
||||
__metadata("design:paramtypes", [typeof (_a = (typeof ng !== "undefined" && ng).ElementRef) === "function" && _a || Object])
|
||||
__metadata("design:paramtypes", [typeof (_a = typeof ng !== "undefined" && ng.ElementRef) === "function" ? _a : Object])
|
||||
], MyClass1);
|
||||
return MyClass1;
|
||||
}());
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ define(["require", "exports", "angular2/core"], function (require, exports, ng)
|
||||
var _a;
|
||||
MyClass1 = __decorate([
|
||||
foo,
|
||||
__metadata("design:paramtypes", [typeof (_a = (typeof ng !== "undefined" && ng).ElementRef) === "function" && _a || Object])
|
||||
__metadata("design:paramtypes", [typeof (_a = typeof ng !== "undefined" && ng.ElementRef) === "function" ? _a : Object])
|
||||
], MyClass1);
|
||||
return MyClass1;
|
||||
}());
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ var MyClass1 = /** @class */ (function () {
|
||||
var _a;
|
||||
MyClass1 = __decorate([
|
||||
foo,
|
||||
__metadata("design:paramtypes", [typeof (_a = (typeof ng !== "undefined" && ng).ElementRef) === "function" && _a || Object])
|
||||
__metadata("design:paramtypes", [typeof (_a = typeof ng !== "undefined" && ng.ElementRef) === "function" ? _a : Object])
|
||||
], MyClass1);
|
||||
return MyClass1;
|
||||
}());
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ var MyClass1 = /** @class */ (function () {
|
||||
var _a;
|
||||
MyClass1 = __decorate([
|
||||
fooexport,
|
||||
__metadata("design:paramtypes", [typeof (_a = (typeof ng !== "undefined" && ng).ElementRef) === "function" && _a || Object])
|
||||
__metadata("design:paramtypes", [typeof (_a = typeof ng !== "undefined" && ng.ElementRef) === "function" ? _a : Object])
|
||||
], MyClass1);
|
||||
return MyClass1;
|
||||
}());
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ var MyClass1 = /** @class */ (function () {
|
||||
var _a;
|
||||
MyClass1 = __decorate([
|
||||
fooexport,
|
||||
__metadata("design:paramtypes", [typeof (_a = (typeof ng !== "undefined" && ng).ElementRef) === "function" && _a || Object])
|
||||
__metadata("design:paramtypes", [typeof (_a = typeof ng !== "undefined" && ng.ElementRef) === "function" ? _a : Object])
|
||||
], MyClass1);
|
||||
return MyClass1;
|
||||
}());
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ System.register(["angular2/core"], function (exports_1, context_1) {
|
||||
var _a;
|
||||
MyClass1 = __decorate([
|
||||
fooexport,
|
||||
__metadata("design:paramtypes", [typeof (_a = (typeof ng !== "undefined" && ng).ElementRef) === "function" && _a || Object])
|
||||
__metadata("design:paramtypes", [typeof (_a = typeof ng !== "undefined" && ng.ElementRef) === "function" ? _a : Object])
|
||||
], MyClass1);
|
||||
return MyClass1;
|
||||
}());
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ System.register(["angular2/core"], function (exports_1, context_1) {
|
||||
var _a;
|
||||
MyClass1 = __decorate([
|
||||
fooexport,
|
||||
__metadata("design:paramtypes", [typeof (_a = (typeof ng !== "undefined" && ng).ElementRef) === "function" && _a || Object])
|
||||
__metadata("design:paramtypes", [typeof (_a = typeof ng !== "undefined" && ng.ElementRef) === "function" ? _a : Object])
|
||||
], MyClass1);
|
||||
return MyClass1;
|
||||
}());
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ var MyClass = /** @class */ (function () {
|
||||
var _a;
|
||||
MyClass = __decorate([
|
||||
someDecorator,
|
||||
__metadata("design:paramtypes", [typeof (_a = typeof db_1.db !== "undefined" && db_1.db) === "function" && _a || Object])
|
||||
__metadata("design:paramtypes", [typeof (_a = typeof db_1.db !== "undefined" && db_1.db) === "function" ? _a : Object])
|
||||
], MyClass);
|
||||
return MyClass;
|
||||
}());
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ var MyClass = /** @class */ (function () {
|
||||
var _a;
|
||||
MyClass = __decorate([
|
||||
someDecorator,
|
||||
__metadata("design:paramtypes", [typeof (_a = typeof db_1.db !== "undefined" && db_1.db) === "function" && _a || Object])
|
||||
__metadata("design:paramtypes", [typeof (_a = typeof db_1.db !== "undefined" && db_1.db) === "function" ? _a : Object])
|
||||
], MyClass);
|
||||
return MyClass;
|
||||
}());
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
// @experimentalDecorators: true
|
||||
// @emitDecoratorMetadata: true
|
||||
// @filename: types.d.ts
|
||||
declare namespace A {
|
||||
export namespace B {
|
||||
export namespace C {
|
||||
export namespace D {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// @filename: usage.ts
|
||||
class Foo {
|
||||
f(@decorate user: A.B.C.D.E): void {}
|
||||
}
|
||||
Reference in New Issue
Block a user