mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-04 21:53:42 -06:00
Classes can extend Javascript constructor functions (#26452)
* Classes can extend JS constructor functions Now ES6 classes can extend ES5 constructor functions, although only those written in a JS file. Note that the static side assignability is checked. I need to write tests to make sure that instance side assignability is checked too. I haven't tested generic constructor functions yet either. * Test static+instance assignability errors+generics Note that generics do not work. * Cleanup from PR comments * Even more cleanup * Update case of function name
This commit is contained in:
parent
62e6e6ae27
commit
29ca93ba48
@ -3291,7 +3291,7 @@ namespace ts {
|
||||
if (symbol) {
|
||||
const isConstructorObject = getObjectFlags(type) & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & SymbolFlags.Class;
|
||||
id = (isConstructorObject ? "+" : "") + getSymbolId(symbol);
|
||||
if (isJavaScriptConstructor(symbol.valueDeclaration)) {
|
||||
if (isJavascriptConstructor(symbol.valueDeclaration)) {
|
||||
// Instance and static types share the same symbol; only add 'typeof' for the static side.
|
||||
const isInstanceType = type === getInferredClassType(symbol) ? SymbolFlags.Type : SymbolFlags.Value;
|
||||
return symbolToTypeNode(symbol, context, isInstanceType);
|
||||
@ -5501,7 +5501,7 @@ namespace ts {
|
||||
const constraint = getBaseConstraintOfType(type);
|
||||
return !!constraint && isValidBaseType(constraint) && isMixinConstructorType(constraint);
|
||||
}
|
||||
return false;
|
||||
return isJavascriptConstructorType(type);
|
||||
}
|
||||
|
||||
function getBaseTypeNodeOfClass(type: InterfaceType): ExpressionWithTypeArguments | undefined {
|
||||
@ -5510,9 +5510,12 @@ namespace ts {
|
||||
|
||||
function getConstructorsForTypeArguments(type: Type, typeArgumentNodes: ReadonlyArray<TypeNode> | undefined, location: Node): ReadonlyArray<Signature> {
|
||||
const typeArgCount = length(typeArgumentNodes);
|
||||
const isJavaScript = isInJavaScriptFile(location);
|
||||
const isJavascript = isInJavaScriptFile(location);
|
||||
if (isJavascriptConstructorType(type) && !typeArgCount) {
|
||||
return getSignaturesOfType(type, SignatureKind.Call);
|
||||
}
|
||||
return filter(getSignaturesOfType(type, SignatureKind.Construct),
|
||||
sig => (isJavaScript || typeArgCount >= getMinTypeArgumentCount(sig.typeParameters)) && typeArgCount <= length(sig.typeParameters));
|
||||
sig => (isJavascript || typeArgCount >= getMinTypeArgumentCount(sig.typeParameters)) && typeArgCount <= length(sig.typeParameters));
|
||||
}
|
||||
|
||||
function getInstantiatedConstructorsForTypeArguments(type: Type, typeArgumentNodes: ReadonlyArray<TypeNode> | undefined, location: Node): ReadonlyArray<Signature> {
|
||||
@ -5603,6 +5606,9 @@ namespace ts {
|
||||
else if (baseConstructorType.flags & TypeFlags.Any) {
|
||||
baseType = baseConstructorType;
|
||||
}
|
||||
else if (isJavascriptConstructorType(baseConstructorType) && !baseTypeNode.typeArguments) {
|
||||
baseType = getJavascriptClassType(baseConstructorType.symbol) || anyType;
|
||||
}
|
||||
else {
|
||||
// The class derives from a "class-like" constructor function, check that we have at least one construct signature
|
||||
// with a matching number of type parameters and use the return type of the first instantiated signature. Elsewhere
|
||||
@ -10110,7 +10116,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
let outerTypeParameters = getOuterTypeParameters(declaration, /*includeThisTypes*/ true);
|
||||
if (isJavaScriptConstructor(declaration)) {
|
||||
if (isJavascriptConstructor(declaration)) {
|
||||
const templateTagParameters = getTypeParametersFromDeclaration(declaration as DeclarationWithTypeParameters);
|
||||
outerTypeParameters = addRange(outerTypeParameters, templateTagParameters);
|
||||
}
|
||||
@ -10407,7 +10413,7 @@ namespace ts {
|
||||
function getTypeWithoutSignatures(type: Type): Type {
|
||||
if (type.flags & TypeFlags.Object) {
|
||||
const resolved = resolveStructuredTypeMembers(<ObjectType>type);
|
||||
if (resolved.constructSignatures.length) {
|
||||
if (resolved.constructSignatures.length || resolved.callSignatures.length) {
|
||||
const result = createObjectType(ObjectFlags.Anonymous, type.symbol);
|
||||
result.members = resolved.members;
|
||||
result.properties = resolved.properties;
|
||||
@ -10741,13 +10747,13 @@ namespace ts {
|
||||
}
|
||||
|
||||
if (!ignoreReturnTypes) {
|
||||
const targetReturnType = (target.declaration && isJavaScriptConstructor(target.declaration)) ?
|
||||
getJavaScriptClassType(target.declaration.symbol)! : getReturnTypeOfSignature(target);
|
||||
const targetReturnType = (target.declaration && isJavascriptConstructor(target.declaration)) ?
|
||||
getJavascriptClassType(target.declaration.symbol)! : getReturnTypeOfSignature(target);
|
||||
if (targetReturnType === voidType) {
|
||||
return result;
|
||||
}
|
||||
const sourceReturnType = (source.declaration && isJavaScriptConstructor(source.declaration)) ?
|
||||
getJavaScriptClassType(source.declaration.symbol)! : getReturnTypeOfSignature(source);
|
||||
const sourceReturnType = (source.declaration && isJavascriptConstructor(source.declaration)) ?
|
||||
getJavascriptClassType(source.declaration.symbol)! : getReturnTypeOfSignature(source);
|
||||
|
||||
// The following block preserves behavior forbidding boolean returning functions from being assignable to type guard returning functions
|
||||
const targetTypePredicate = getTypePredicateOfSignature(target);
|
||||
@ -12014,8 +12020,8 @@ namespace ts {
|
||||
return Ternary.True;
|
||||
}
|
||||
|
||||
const sourceIsJSConstructor = source.symbol && isJavaScriptConstructor(source.symbol.valueDeclaration);
|
||||
const targetIsJSConstructor = target.symbol && isJavaScriptConstructor(target.symbol.valueDeclaration);
|
||||
const sourceIsJSConstructor = source.symbol && isJavascriptConstructor(source.symbol.valueDeclaration);
|
||||
const targetIsJSConstructor = target.symbol && isJavascriptConstructor(target.symbol.valueDeclaration);
|
||||
|
||||
const sourceSignatures = getSignaturesOfType(source, (sourceIsJSConstructor && kind === SignatureKind.Construct) ?
|
||||
SignatureKind.Call : kind);
|
||||
@ -15500,7 +15506,7 @@ namespace ts {
|
||||
// * /** @constructor */ var x = function() { ... }
|
||||
else if ((container.kind === SyntaxKind.FunctionExpression || container.kind === SyntaxKind.FunctionDeclaration) &&
|
||||
getJSDocClassTag(container)) {
|
||||
const classType = getJavaScriptClassType(container.symbol);
|
||||
const classType = getJavascriptClassType(container.symbol);
|
||||
if (classType) {
|
||||
return getFlowTypeOfReference(node, classType);
|
||||
}
|
||||
@ -19660,7 +19666,7 @@ namespace ts {
|
||||
const callSignatures = getSignaturesOfType(expressionType, SignatureKind.Call);
|
||||
if (callSignatures.length) {
|
||||
const signature = resolveCall(node, callSignatures, candidatesOutArray, isForSignatureHelp);
|
||||
if (signature.declaration && !isJavaScriptConstructor(signature.declaration) && getReturnTypeOfSignature(signature) !== voidType) {
|
||||
if (signature.declaration && !isJavascriptConstructor(signature.declaration) && getReturnTypeOfSignature(signature) !== voidType) {
|
||||
error(node, Diagnostics.Only_a_void_function_can_be_called_with_the_new_keyword);
|
||||
}
|
||||
if (getThisTypeOfSignature(signature) === voidType) {
|
||||
@ -19941,7 +19947,7 @@ namespace ts {
|
||||
* Indicates whether a declaration can be treated as a constructor in a JavaScript
|
||||
* file.
|
||||
*/
|
||||
function isJavaScriptConstructor(node: Declaration | undefined): boolean {
|
||||
function isJavascriptConstructor(node: Declaration | undefined): boolean {
|
||||
if (node && isInJavaScriptFile(node)) {
|
||||
// If the node has a @class tag, treat it like a constructor.
|
||||
if (getJSDocClassTag(node)) return true;
|
||||
@ -19957,14 +19963,22 @@ namespace ts {
|
||||
return false;
|
||||
}
|
||||
|
||||
function getJavaScriptClassType(symbol: Symbol): Type | undefined {
|
||||
function isJavascriptConstructorType(type: Type) {
|
||||
if (type.flags & TypeFlags.Object) {
|
||||
const resolved = resolveStructuredTypeMembers(<ObjectType>type);
|
||||
return resolved.callSignatures.length === 1 && isJavascriptConstructor(resolved.callSignatures[0].declaration);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function getJavascriptClassType(symbol: Symbol): Type | undefined {
|
||||
let inferred: Type | undefined;
|
||||
if (isJavaScriptConstructor(symbol.valueDeclaration)) {
|
||||
if (isJavascriptConstructor(symbol.valueDeclaration)) {
|
||||
inferred = getInferredClassType(symbol);
|
||||
}
|
||||
const assigned = getAssignedClassType(symbol);
|
||||
const valueType = getTypeOfSymbol(symbol);
|
||||
if (valueType.symbol && !isInferredClassType(valueType) && isJavaScriptConstructor(valueType.symbol.valueDeclaration)) {
|
||||
if (valueType.symbol && !isInferredClassType(valueType) && isJavascriptConstructor(valueType.symbol.valueDeclaration)) {
|
||||
inferred = getInferredClassType(valueType.symbol);
|
||||
}
|
||||
return assigned && inferred ?
|
||||
@ -20047,7 +20061,7 @@ namespace ts {
|
||||
if (!funcSymbol && node.expression.kind === SyntaxKind.Identifier) {
|
||||
funcSymbol = getResolvedSymbol(node.expression as Identifier);
|
||||
}
|
||||
const type = funcSymbol && getJavaScriptClassType(funcSymbol);
|
||||
const type = funcSymbol && getJavascriptClassType(funcSymbol);
|
||||
if (type) {
|
||||
return signature.target ? instantiateType(type, signature.mapper) : type;
|
||||
}
|
||||
@ -20655,7 +20669,7 @@ namespace ts {
|
||||
return undefined;
|
||||
}
|
||||
if (strictNullChecks && aggregatedTypes.length && hasReturnWithNoExpression &&
|
||||
!(isJavaScriptConstructor(func) && aggregatedTypes.some(t => t.symbol === func.symbol))) {
|
||||
!(isJavascriptConstructor(func) && aggregatedTypes.some(t => t.symbol === func.symbol))) {
|
||||
// Javascript "callable constructors", containing eg `if (!(this instanceof A)) return new A()` should not add undefined
|
||||
pushIfUnique(aggregatedTypes, undefinedType);
|
||||
}
|
||||
@ -25561,8 +25575,9 @@ namespace ts {
|
||||
// that all instantiated base constructor signatures return the same type. We can simply compare the type
|
||||
// references (as opposed to checking the structure of the types) because elsewhere we have already checked
|
||||
// that the base type is a class or interface type (and not, for example, an anonymous object type).
|
||||
// (Javascript constructor functions have this property trivially true since their return type is ignored.)
|
||||
const constructors = getInstantiatedConstructorsForTypeArguments(staticBaseType, baseTypeNode.typeArguments, baseTypeNode);
|
||||
if (forEach(constructors, sig => getReturnTypeOfSignature(sig) !== baseType)) {
|
||||
if (forEach(constructors, sig => !isJavascriptConstructor(sig.declaration) && getReturnTypeOfSignature(sig) !== baseType)) {
|
||||
error(baseTypeNode.expression, Diagnostics.Base_constructors_must_all_have_the_same_return_type);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,132 @@
|
||||
tests/cases/conformance/salsa/first.js(18,9): error TS2554: Expected 1 arguments, but got 0.
|
||||
tests/cases/conformance/salsa/first.js(26,5): error TS2416: Property 'load' in type 'Sql' is not assignable to the same property in base type 'Wagon'.
|
||||
Type '(files: string[], format: "csv" | "json" | "xmlolololol") => void' is not assignable to type '(supplies?: any[]) => void'.
|
||||
tests/cases/conformance/salsa/first.js(36,24): error TS2507: Type '(numberEaten: number) => void' is not a constructor function type.
|
||||
tests/cases/conformance/salsa/generic.js(8,15): error TS2508: No base constructor has the specified number of type arguments.
|
||||
tests/cases/conformance/salsa/generic.js(11,21): error TS2339: Property 'flavour' does not exist on type 'Chowder'.
|
||||
tests/cases/conformance/salsa/generic.js(18,9): error TS2339: Property 'flavour' does not exist on type 'Chowder'.
|
||||
tests/cases/conformance/salsa/second.ts(8,25): error TS2507: Type '(numberEaten: number) => void' is not a constructor function type.
|
||||
tests/cases/conformance/salsa/second.ts(14,7): error TS2417: Class static side 'typeof Conestoga' incorrectly extends base class static side 'typeof Wagon'.
|
||||
Types of property 'circle' are incompatible.
|
||||
Type '(others: (typeof Wagon)[]) => number' is not assignable to type '(wagons?: Wagon[]) => number'.
|
||||
Types of parameters 'others' and 'wagons' are incompatible.
|
||||
Type 'Wagon[]' is not assignable to type '(typeof Wagon)[]'.
|
||||
Type 'Wagon' is not assignable to type 'typeof Wagon'.
|
||||
Property 'circle' is missing in type 'Wagon'.
|
||||
tests/cases/conformance/salsa/second.ts(17,15): error TS2345: Argument of type '"nope"' is not assignable to parameter of type 'number'.
|
||||
|
||||
|
||||
==== tests/cases/conformance/salsa/first.js (3 errors) ====
|
||||
/**
|
||||
* @constructor
|
||||
* @param {number} numberOxen
|
||||
*/
|
||||
function Wagon(numberOxen) {
|
||||
this.numberOxen = numberOxen
|
||||
}
|
||||
/** @param {Wagon[]=} wagons */
|
||||
Wagon.circle = function (wagons) {
|
||||
return wagons ? wagons.length : 3.14;
|
||||
}
|
||||
/** @param {*[]=} supplies - *[]= is my favourite type */
|
||||
Wagon.prototype.load = function (supplies) {
|
||||
}
|
||||
// ok
|
||||
class Sql extends Wagon {
|
||||
constructor() {
|
||||
super(); // error: not enough arguments
|
||||
~~~~~~~
|
||||
!!! error TS2554: Expected 1 arguments, but got 0.
|
||||
this.foonly = 12
|
||||
}
|
||||
/**
|
||||
* @param {Array.<string>} files
|
||||
* @param {"csv" | "json" | "xmlolololol"} format
|
||||
* This is not assignable, so should have a type error
|
||||
*/
|
||||
load(files, format) {
|
||||
~~~~
|
||||
!!! error TS2416: Property 'load' in type 'Sql' is not assignable to the same property in base type 'Wagon'.
|
||||
!!! error TS2416: Type '(files: string[], format: "csv" | "json" | "xmlolololol") => void' is not assignable to type '(supplies?: any[]) => void'.
|
||||
if (format === "xmlolololol") {
|
||||
throw new Error("please do not use XML. It was a joke.");
|
||||
}
|
||||
}
|
||||
}
|
||||
var db = new Sql();
|
||||
db.numberOxen = db.foonly
|
||||
|
||||
// error, can't extend a TS constructor function
|
||||
class Drakkhen extends Dragon {
|
||||
~~~~~~
|
||||
!!! error TS2507: Type '(numberEaten: number) => void' is not a constructor function type.
|
||||
|
||||
}
|
||||
|
||||
==== tests/cases/conformance/salsa/second.ts (3 errors) ====
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
function Dragon(numberEaten: number) {
|
||||
this.numberEaten = numberEaten
|
||||
}
|
||||
// error!
|
||||
class Firedrake extends Dragon {
|
||||
~~~~~~
|
||||
!!! error TS2507: Type '(numberEaten: number) => void' is not a constructor function type.
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
}
|
||||
// ok
|
||||
class Conestoga extends Wagon {
|
||||
~~~~~~~~~
|
||||
!!! error TS2417: Class static side 'typeof Conestoga' incorrectly extends base class static side 'typeof Wagon'.
|
||||
!!! error TS2417: Types of property 'circle' are incompatible.
|
||||
!!! error TS2417: Type '(others: (typeof Wagon)[]) => number' is not assignable to type '(wagons?: Wagon[]) => number'.
|
||||
!!! error TS2417: Types of parameters 'others' and 'wagons' are incompatible.
|
||||
!!! error TS2417: Type 'Wagon[]' is not assignable to type '(typeof Wagon)[]'.
|
||||
!!! error TS2417: Type 'Wagon' is not assignable to type 'typeof Wagon'.
|
||||
!!! error TS2417: Property 'circle' is missing in type 'Wagon'.
|
||||
constructor(public drunkOO: true) {
|
||||
// error: wrong type
|
||||
super('nope');
|
||||
~~~~~~
|
||||
!!! error TS2345: Argument of type '"nope"' is not assignable to parameter of type 'number'.
|
||||
}
|
||||
// should error since others is not optional
|
||||
static circle(others: (typeof Wagon)[]) {
|
||||
return others.length
|
||||
}
|
||||
}
|
||||
var c = new Conestoga(true);
|
||||
c.drunkOO
|
||||
c.numberOxen
|
||||
|
||||
==== tests/cases/conformance/salsa/generic.js (3 errors) ====
|
||||
/**
|
||||
* @template T
|
||||
* @param {T} flavour
|
||||
*/
|
||||
function Soup(flavour) {
|
||||
this.flavour = flavour
|
||||
}
|
||||
/** @extends {Soup<{ claim: "ignorant" | "malicious" }>} */
|
||||
~~~~
|
||||
!!! error TS2508: No base constructor has the specified number of type arguments.
|
||||
class Chowder extends Soup {
|
||||
log() {
|
||||
return this.flavour
|
||||
~~~~~~~
|
||||
!!! error TS2339: Property 'flavour' does not exist on type 'Chowder'.
|
||||
}
|
||||
}
|
||||
|
||||
var soup = new Soup(1);
|
||||
soup.flavour
|
||||
var chowder = new Chowder();
|
||||
chowder.flavour.claim
|
||||
~~~~~~~
|
||||
!!! error TS2339: Property 'flavour' does not exist on type 'Chowder'.
|
||||
|
||||
|
||||
@ -0,0 +1,188 @@
|
||||
=== tests/cases/conformance/salsa/first.js ===
|
||||
/**
|
||||
* @constructor
|
||||
* @param {number} numberOxen
|
||||
*/
|
||||
function Wagon(numberOxen) {
|
||||
>Wagon : Symbol(Wagon, Decl(first.js, 0, 0), Decl(first.js, 6, 1))
|
||||
>numberOxen : Symbol(numberOxen, Decl(first.js, 4, 15))
|
||||
|
||||
this.numberOxen = numberOxen
|
||||
>this.numberOxen : Symbol(Wagon.numberOxen, Decl(first.js, 4, 28))
|
||||
>this : Symbol(Wagon, Decl(first.js, 0, 0), Decl(first.js, 6, 1))
|
||||
>numberOxen : Symbol(Wagon.numberOxen, Decl(first.js, 4, 28))
|
||||
>numberOxen : Symbol(numberOxen, Decl(first.js, 4, 15))
|
||||
}
|
||||
/** @param {Wagon[]=} wagons */
|
||||
Wagon.circle = function (wagons) {
|
||||
>Wagon.circle : Symbol(Wagon.circle, Decl(first.js, 6, 1))
|
||||
>Wagon : Symbol(Wagon, Decl(first.js, 0, 0), Decl(first.js, 6, 1))
|
||||
>circle : Symbol(Wagon.circle, Decl(first.js, 6, 1))
|
||||
>wagons : Symbol(wagons, Decl(first.js, 8, 25))
|
||||
|
||||
return wagons ? wagons.length : 3.14;
|
||||
>wagons : Symbol(wagons, Decl(first.js, 8, 25))
|
||||
>wagons.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
|
||||
>wagons : Symbol(wagons, Decl(first.js, 8, 25))
|
||||
>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
|
||||
}
|
||||
/** @param {*[]=} supplies - *[]= is my favourite type */
|
||||
Wagon.prototype.load = function (supplies) {
|
||||
>Wagon.prototype : Symbol(Wagon.load, Decl(first.js, 10, 1))
|
||||
>Wagon : Symbol(Wagon, Decl(first.js, 0, 0), Decl(first.js, 6, 1))
|
||||
>prototype : Symbol(Function.prototype, Decl(lib.es5.d.ts, --, --))
|
||||
>load : Symbol(Wagon.load, Decl(first.js, 10, 1))
|
||||
>supplies : Symbol(supplies, Decl(first.js, 12, 33))
|
||||
}
|
||||
// ok
|
||||
class Sql extends Wagon {
|
||||
>Sql : Symbol(Sql, Decl(first.js, 13, 1))
|
||||
>Wagon : Symbol(Wagon, Decl(first.js, 0, 0), Decl(first.js, 6, 1))
|
||||
|
||||
constructor() {
|
||||
super(); // error: not enough arguments
|
||||
>super : Symbol(Wagon, Decl(first.js, 0, 0), Decl(first.js, 6, 1))
|
||||
|
||||
this.foonly = 12
|
||||
>this.foonly : Symbol(Sql.foonly, Decl(first.js, 17, 16))
|
||||
>this : Symbol(Sql, Decl(first.js, 13, 1))
|
||||
>foonly : Symbol(Sql.foonly, Decl(first.js, 17, 16))
|
||||
}
|
||||
/**
|
||||
* @param {Array.<string>} files
|
||||
* @param {"csv" | "json" | "xmlolololol"} format
|
||||
* This is not assignable, so should have a type error
|
||||
*/
|
||||
load(files, format) {
|
||||
>load : Symbol(Sql.load, Decl(first.js, 19, 5))
|
||||
>files : Symbol(files, Decl(first.js, 25, 9))
|
||||
>format : Symbol(format, Decl(first.js, 25, 15))
|
||||
|
||||
if (format === "xmlolololol") {
|
||||
>format : Symbol(format, Decl(first.js, 25, 15))
|
||||
|
||||
throw new Error("please do not use XML. It was a joke.");
|
||||
>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
}
|
||||
}
|
||||
}
|
||||
var db = new Sql();
|
||||
>db : Symbol(db, Decl(first.js, 31, 3))
|
||||
>Sql : Symbol(Sql, Decl(first.js, 13, 1))
|
||||
|
||||
db.numberOxen = db.foonly
|
||||
>db.numberOxen : Symbol(Wagon.numberOxen, Decl(first.js, 4, 28))
|
||||
>db : Symbol(db, Decl(first.js, 31, 3))
|
||||
>numberOxen : Symbol(Wagon.numberOxen, Decl(first.js, 4, 28))
|
||||
>db.foonly : Symbol(Sql.foonly, Decl(first.js, 17, 16))
|
||||
>db : Symbol(db, Decl(first.js, 31, 3))
|
||||
>foonly : Symbol(Sql.foonly, Decl(first.js, 17, 16))
|
||||
|
||||
// error, can't extend a TS constructor function
|
||||
class Drakkhen extends Dragon {
|
||||
>Drakkhen : Symbol(Drakkhen, Decl(first.js, 32, 25))
|
||||
>Dragon : Symbol(Dragon, Decl(second.ts, 0, 0))
|
||||
|
||||
}
|
||||
|
||||
=== tests/cases/conformance/salsa/second.ts ===
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
function Dragon(numberEaten: number) {
|
||||
>Dragon : Symbol(Dragon, Decl(second.ts, 0, 0))
|
||||
>numberEaten : Symbol(numberEaten, Decl(second.ts, 3, 16))
|
||||
|
||||
this.numberEaten = numberEaten
|
||||
>numberEaten : Symbol(numberEaten, Decl(second.ts, 3, 16))
|
||||
}
|
||||
// error!
|
||||
class Firedrake extends Dragon {
|
||||
>Firedrake : Symbol(Firedrake, Decl(second.ts, 5, 1))
|
||||
>Dragon : Symbol(Dragon, Decl(second.ts, 0, 0))
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
}
|
||||
// ok
|
||||
class Conestoga extends Wagon {
|
||||
>Conestoga : Symbol(Conestoga, Decl(second.ts, 11, 1))
|
||||
>Wagon : Symbol(Wagon, Decl(first.js, 0, 0), Decl(first.js, 6, 1))
|
||||
|
||||
constructor(public drunkOO: true) {
|
||||
>drunkOO : Symbol(Conestoga.drunkOO, Decl(second.ts, 14, 16))
|
||||
|
||||
// error: wrong type
|
||||
super('nope');
|
||||
>super : Symbol(Wagon, Decl(first.js, 0, 0), Decl(first.js, 6, 1))
|
||||
}
|
||||
// should error since others is not optional
|
||||
static circle(others: (typeof Wagon)[]) {
|
||||
>circle : Symbol(Conestoga.circle, Decl(second.ts, 17, 5))
|
||||
>others : Symbol(others, Decl(second.ts, 19, 18))
|
||||
>Wagon : Symbol(Wagon, Decl(first.js, 0, 0), Decl(first.js, 6, 1))
|
||||
|
||||
return others.length
|
||||
>others.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
|
||||
>others : Symbol(others, Decl(second.ts, 19, 18))
|
||||
>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
|
||||
}
|
||||
}
|
||||
var c = new Conestoga(true);
|
||||
>c : Symbol(c, Decl(second.ts, 23, 3))
|
||||
>Conestoga : Symbol(Conestoga, Decl(second.ts, 11, 1))
|
||||
|
||||
c.drunkOO
|
||||
>c.drunkOO : Symbol(Conestoga.drunkOO, Decl(second.ts, 14, 16))
|
||||
>c : Symbol(c, Decl(second.ts, 23, 3))
|
||||
>drunkOO : Symbol(Conestoga.drunkOO, Decl(second.ts, 14, 16))
|
||||
|
||||
c.numberOxen
|
||||
>c.numberOxen : Symbol(Wagon.numberOxen, Decl(first.js, 4, 28))
|
||||
>c : Symbol(c, Decl(second.ts, 23, 3))
|
||||
>numberOxen : Symbol(Wagon.numberOxen, Decl(first.js, 4, 28))
|
||||
|
||||
=== tests/cases/conformance/salsa/generic.js ===
|
||||
/**
|
||||
* @template T
|
||||
* @param {T} flavour
|
||||
*/
|
||||
function Soup(flavour) {
|
||||
>Soup : Symbol(Soup, Decl(generic.js, 0, 0))
|
||||
>flavour : Symbol(flavour, Decl(generic.js, 4, 14))
|
||||
|
||||
this.flavour = flavour
|
||||
>flavour : Symbol(Soup.flavour, Decl(generic.js, 4, 24))
|
||||
>flavour : Symbol(flavour, Decl(generic.js, 4, 14))
|
||||
}
|
||||
/** @extends {Soup<{ claim: "ignorant" | "malicious" }>} */
|
||||
class Chowder extends Soup {
|
||||
>Chowder : Symbol(Chowder, Decl(generic.js, 6, 1))
|
||||
>Soup : Symbol(Soup, Decl(generic.js, 0, 0))
|
||||
|
||||
log() {
|
||||
>log : Symbol(Chowder.log, Decl(generic.js, 8, 28))
|
||||
|
||||
return this.flavour
|
||||
>this : Symbol(Chowder, Decl(generic.js, 6, 1))
|
||||
}
|
||||
}
|
||||
|
||||
var soup = new Soup(1);
|
||||
>soup : Symbol(soup, Decl(generic.js, 14, 3))
|
||||
>Soup : Symbol(Soup, Decl(generic.js, 0, 0))
|
||||
|
||||
soup.flavour
|
||||
>soup.flavour : Symbol(Soup.flavour, Decl(generic.js, 4, 24))
|
||||
>soup : Symbol(soup, Decl(generic.js, 14, 3))
|
||||
>flavour : Symbol(Soup.flavour, Decl(generic.js, 4, 24))
|
||||
|
||||
var chowder = new Chowder();
|
||||
>chowder : Symbol(chowder, Decl(generic.js, 16, 3))
|
||||
>Chowder : Symbol(Chowder, Decl(generic.js, 6, 1))
|
||||
|
||||
chowder.flavour.claim
|
||||
>chowder : Symbol(chowder, Decl(generic.js, 16, 3))
|
||||
|
||||
|
||||
@ -0,0 +1,228 @@
|
||||
=== tests/cases/conformance/salsa/first.js ===
|
||||
/**
|
||||
* @constructor
|
||||
* @param {number} numberOxen
|
||||
*/
|
||||
function Wagon(numberOxen) {
|
||||
>Wagon : typeof Wagon
|
||||
>numberOxen : number
|
||||
|
||||
this.numberOxen = numberOxen
|
||||
>this.numberOxen = numberOxen : number
|
||||
>this.numberOxen : number
|
||||
>this : Wagon
|
||||
>numberOxen : number
|
||||
>numberOxen : number
|
||||
}
|
||||
/** @param {Wagon[]=} wagons */
|
||||
Wagon.circle = function (wagons) {
|
||||
>Wagon.circle = function (wagons) { return wagons ? wagons.length : 3.14;} : (wagons?: Wagon[]) => number
|
||||
>Wagon.circle : (wagons?: Wagon[]) => number
|
||||
>Wagon : typeof Wagon
|
||||
>circle : (wagons?: Wagon[]) => number
|
||||
>function (wagons) { return wagons ? wagons.length : 3.14;} : (wagons?: Wagon[]) => number
|
||||
>wagons : Wagon[]
|
||||
|
||||
return wagons ? wagons.length : 3.14;
|
||||
>wagons ? wagons.length : 3.14 : number
|
||||
>wagons : Wagon[]
|
||||
>wagons.length : number
|
||||
>wagons : Wagon[]
|
||||
>length : number
|
||||
>3.14 : 3.14
|
||||
}
|
||||
/** @param {*[]=} supplies - *[]= is my favourite type */
|
||||
Wagon.prototype.load = function (supplies) {
|
||||
>Wagon.prototype.load = function (supplies) {} : (supplies?: any[]) => void
|
||||
>Wagon.prototype.load : any
|
||||
>Wagon.prototype : any
|
||||
>Wagon : typeof Wagon
|
||||
>prototype : any
|
||||
>load : any
|
||||
>function (supplies) {} : (supplies?: any[]) => void
|
||||
>supplies : any[]
|
||||
}
|
||||
// ok
|
||||
class Sql extends Wagon {
|
||||
>Sql : Sql
|
||||
>Wagon : Wagon
|
||||
|
||||
constructor() {
|
||||
super(); // error: not enough arguments
|
||||
>super() : void
|
||||
>super : typeof Wagon
|
||||
|
||||
this.foonly = 12
|
||||
>this.foonly = 12 : 12
|
||||
>this.foonly : number
|
||||
>this : this
|
||||
>foonly : number
|
||||
>12 : 12
|
||||
}
|
||||
/**
|
||||
* @param {Array.<string>} files
|
||||
* @param {"csv" | "json" | "xmlolololol"} format
|
||||
* This is not assignable, so should have a type error
|
||||
*/
|
||||
load(files, format) {
|
||||
>load : (files: string[], format: "csv" | "json" | "xmlolololol") => void
|
||||
>files : string[]
|
||||
>format : "csv" | "json" | "xmlolololol"
|
||||
|
||||
if (format === "xmlolololol") {
|
||||
>format === "xmlolololol" : boolean
|
||||
>format : "csv" | "json" | "xmlolololol"
|
||||
>"xmlolololol" : "xmlolololol"
|
||||
|
||||
throw new Error("please do not use XML. It was a joke.");
|
||||
>new Error("please do not use XML. It was a joke.") : Error
|
||||
>Error : ErrorConstructor
|
||||
>"please do not use XML. It was a joke." : "please do not use XML. It was a joke."
|
||||
}
|
||||
}
|
||||
}
|
||||
var db = new Sql();
|
||||
>db : Sql
|
||||
>new Sql() : Sql
|
||||
>Sql : typeof Sql
|
||||
|
||||
db.numberOxen = db.foonly
|
||||
>db.numberOxen = db.foonly : number
|
||||
>db.numberOxen : number
|
||||
>db : Sql
|
||||
>numberOxen : number
|
||||
>db.foonly : number
|
||||
>db : Sql
|
||||
>foonly : number
|
||||
|
||||
// error, can't extend a TS constructor function
|
||||
class Drakkhen extends Dragon {
|
||||
>Drakkhen : Drakkhen
|
||||
>Dragon : (numberEaten: number) => void
|
||||
|
||||
}
|
||||
|
||||
=== tests/cases/conformance/salsa/second.ts ===
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
function Dragon(numberEaten: number) {
|
||||
>Dragon : (numberEaten: number) => void
|
||||
>numberEaten : number
|
||||
|
||||
this.numberEaten = numberEaten
|
||||
>this.numberEaten = numberEaten : number
|
||||
>this.numberEaten : any
|
||||
>this : any
|
||||
>numberEaten : any
|
||||
>numberEaten : number
|
||||
}
|
||||
// error!
|
||||
class Firedrake extends Dragon {
|
||||
>Firedrake : Firedrake
|
||||
>Dragon : (numberEaten: number) => void
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
>super() : void
|
||||
>super : any
|
||||
}
|
||||
}
|
||||
// ok
|
||||
class Conestoga extends Wagon {
|
||||
>Conestoga : Conestoga
|
||||
>Wagon : Wagon
|
||||
|
||||
constructor(public drunkOO: true) {
|
||||
>drunkOO : true
|
||||
>true : true
|
||||
|
||||
// error: wrong type
|
||||
super('nope');
|
||||
>super('nope') : void
|
||||
>super : typeof Wagon
|
||||
>'nope' : "nope"
|
||||
}
|
||||
// should error since others is not optional
|
||||
static circle(others: (typeof Wagon)[]) {
|
||||
>circle : (others: (typeof Wagon)[]) => number
|
||||
>others : (typeof Wagon)[]
|
||||
>Wagon : typeof Wagon
|
||||
|
||||
return others.length
|
||||
>others.length : number
|
||||
>others : (typeof Wagon)[]
|
||||
>length : number
|
||||
}
|
||||
}
|
||||
var c = new Conestoga(true);
|
||||
>c : Conestoga
|
||||
>new Conestoga(true) : Conestoga
|
||||
>Conestoga : typeof Conestoga
|
||||
>true : true
|
||||
|
||||
c.drunkOO
|
||||
>c.drunkOO : true
|
||||
>c : Conestoga
|
||||
>drunkOO : true
|
||||
|
||||
c.numberOxen
|
||||
>c.numberOxen : number
|
||||
>c : Conestoga
|
||||
>numberOxen : number
|
||||
|
||||
=== tests/cases/conformance/salsa/generic.js ===
|
||||
/**
|
||||
* @template T
|
||||
* @param {T} flavour
|
||||
*/
|
||||
function Soup(flavour) {
|
||||
>Soup : typeof Soup
|
||||
>flavour : T
|
||||
|
||||
this.flavour = flavour
|
||||
>this.flavour = flavour : T
|
||||
>this.flavour : any
|
||||
>this : any
|
||||
>flavour : any
|
||||
>flavour : T
|
||||
}
|
||||
/** @extends {Soup<{ claim: "ignorant" | "malicious" }>} */
|
||||
class Chowder extends Soup {
|
||||
>Chowder : Chowder
|
||||
>Soup : typeof Soup
|
||||
|
||||
log() {
|
||||
>log : () => any
|
||||
|
||||
return this.flavour
|
||||
>this.flavour : any
|
||||
>this : this
|
||||
>flavour : any
|
||||
}
|
||||
}
|
||||
|
||||
var soup = new Soup(1);
|
||||
>soup : typeof Soup
|
||||
>new Soup(1) : typeof Soup
|
||||
>Soup : typeof Soup
|
||||
>1 : 1
|
||||
|
||||
soup.flavour
|
||||
>soup.flavour : number
|
||||
>soup : typeof Soup
|
||||
>flavour : number
|
||||
|
||||
var chowder = new Chowder();
|
||||
>chowder : Chowder
|
||||
>new Chowder() : Chowder
|
||||
>Chowder : typeof Chowder
|
||||
|
||||
chowder.flavour.claim
|
||||
>chowder.flavour.claim : any
|
||||
>chowder.flavour : any
|
||||
>chowder : Chowder
|
||||
>flavour : any
|
||||
>claim : any
|
||||
|
||||
|
||||
@ -0,0 +1,94 @@
|
||||
// @noEmit: true
|
||||
// @allowJs: true
|
||||
// @checkJs: true
|
||||
// @Filename: first.js
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @param {number} numberOxen
|
||||
*/
|
||||
function Wagon(numberOxen) {
|
||||
this.numberOxen = numberOxen
|
||||
}
|
||||
/** @param {Wagon[]=} wagons */
|
||||
Wagon.circle = function (wagons) {
|
||||
return wagons ? wagons.length : 3.14;
|
||||
}
|
||||
/** @param {*[]=} supplies - *[]= is my favourite type */
|
||||
Wagon.prototype.load = function (supplies) {
|
||||
}
|
||||
// ok
|
||||
class Sql extends Wagon {
|
||||
constructor() {
|
||||
super(); // error: not enough arguments
|
||||
this.foonly = 12
|
||||
}
|
||||
/**
|
||||
* @param {Array.<string>} files
|
||||
* @param {"csv" | "json" | "xmlolololol"} format
|
||||
* This is not assignable, so should have a type error
|
||||
*/
|
||||
load(files, format) {
|
||||
if (format === "xmlolololol") {
|
||||
throw new Error("please do not use XML. It was a joke.");
|
||||
}
|
||||
}
|
||||
}
|
||||
var db = new Sql();
|
||||
db.numberOxen = db.foonly
|
||||
|
||||
// error, can't extend a TS constructor function
|
||||
class Drakkhen extends Dragon {
|
||||
|
||||
}
|
||||
|
||||
// @Filename: second.ts
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
function Dragon(numberEaten: number) {
|
||||
this.numberEaten = numberEaten
|
||||
}
|
||||
// error!
|
||||
class Firedrake extends Dragon {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
}
|
||||
// ok
|
||||
class Conestoga extends Wagon {
|
||||
constructor(public drunkOO: true) {
|
||||
// error: wrong type
|
||||
super('nope');
|
||||
}
|
||||
// should error since others is not optional
|
||||
static circle(others: (typeof Wagon)[]) {
|
||||
return others.length
|
||||
}
|
||||
}
|
||||
var c = new Conestoga(true);
|
||||
c.drunkOO
|
||||
c.numberOxen
|
||||
|
||||
// @Filename: generic.js
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param {T} flavour
|
||||
*/
|
||||
function Soup(flavour) {
|
||||
this.flavour = flavour
|
||||
}
|
||||
/** @extends {Soup<{ claim: "ignorant" | "malicious" }>} */
|
||||
class Chowder extends Soup {
|
||||
log() {
|
||||
return this.flavour
|
||||
}
|
||||
}
|
||||
|
||||
var soup = new Soup(1);
|
||||
soup.flavour
|
||||
var chowder = new Chowder();
|
||||
chowder.flavour.claim
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user