diff --git a/src-runtime/validateType.js b/src-runtime/validateType.js index 7f3eecc..b654fb9 100644 --- a/src-runtime/validateType.js +++ b/src-runtime/validateType.js @@ -84,9 +84,6 @@ function validateType(value, expect, loc, name, critical = true, warn, depth) { } return ret; } - if (type === "undefined") { - return value === undefined; - } if (typeof value === 'number') { if (isNaN(value)) { warn("value is NaN"); @@ -110,97 +107,86 @@ function validateType(value, expect, loc, name, critical = true, warn, depth) { return false; } } - if (type === "object") { - return validateObject(value, properties, loc, name, critical, warn, depth + 1); - } - if (type === 'record') { - return validateRecord(value, expect, loc, name, critical, warn, depth + 1); - } - if (type === 'map') { - return validateMap(value, expect, loc, name, critical, warn, depth + 1); - } - if (type === 'mapping') { - return validateMapping(value, expect, loc, name, critical, warn, depth + 1); - } - if (type === 'array') { - return validateArray(value, expect, loc, name, critical, warn, depth + 1); - } - if (type === 'intersection') { - return validateIntersection(value, expect, loc, name, critical, warn, depth + 1); - } - if (type === 'keyof') { - return validateKeyof(value, expect, loc, name, critical, warn, depth + 1); - } - // If a typedef is also a class, it's just a shorthand-typedef-class if (typedefs[type] && !classes[type]) { + // If a typedef is also a class, it's just a shorthand-typedef-class return validateTypedef(value, expect, loc, name, critical, warn, depth + 1); } - if (type === 'union') { - return validateUnion(value, expect, loc, name, critical, warn, depth + 1); - } - if (type === 'set') { - return validateSet(value, expect, loc, name, critical, warn, depth + 1); - } - // Trigger: pc.app.scene.setSkybox([1, 2, 3]); - if (type === 'tuple') { - return validateTuple(value, expect, loc, name, critical, warn, depth + 1); - } - if (type === '*' || type === 'any') { - return true; - } - /** @todo allow strict/non-strict null/undefined with checkbox in
*/ - if (type === 'null') { - return value === null; - } - /** @todo add unit-tests/asserts tests to make sure this never happens */ - if (value === null) { - // type !== null already, so this can only be false - return false; - } - /** @todo use validateNumber() */ - if (type === 'number') { - if (Number.isNaN(value)) { + switch (type) { + case 'undefined': + return value === undefined; + case 'object': + return validateObject(value, properties, loc, name, critical, warn, depth + 1); + case 'record': + return validateRecord(value, expect, loc, name, critical, warn, depth + 1); + case 'map': + return validateMap(value, expect, loc, name, critical, warn, depth + 1); + case 'mapping': + return validateMapping(value, expect, loc, name, critical, warn, depth + 1); + case 'array': + return validateArray(value, expect, loc, name, critical, warn, depth + 1); + case 'intersection': + return validateIntersection(value, expect, loc, name, critical, warn, depth + 1); + case 'keyof': + return validateKeyof(value, expect, loc, name, critical, warn, depth + 1); + case 'union': + return validateUnion(value, expect, loc, name, critical, warn, depth + 1); + case 'set': + return validateSet(value, expect, loc, name, critical, warn, depth + 1); + case 'tuple': + // Trigger: pc.app.scene.setSkybox([1, 2, 3]); + return validateTuple(value, expect, loc, name, critical, warn, depth + 1); + case '*': + case 'any': + return true; + case 'null': + /** @todo allow strict/non-strict null/undefined with checkbox in
*/ + return value === null; + case 'number': + /** @todo use validateNumber() */ + if (Number.isNaN(value)) { + return false; + } + return typeof value === type; + case 'string': + case 'boolean': + return typeof value === type; + case 'Function': + case 'function': + case 'new': + return typeof value === 'function'; + case 'ObjectConstructor': + return typeof value.constructor === 'function'; + case 'class': + /** @todo PlayCanvas specific, move into custom validations */ + if (value && expect.elementType === 'ScriptType') { + if (value.name === 'scriptType') { + return true; + } + const proto = Object.getPrototypeOf(value); + if (proto?.name === 'ScriptType') { + return true; + } + } + warn(`${loc}> validateType> class> expected object, not '${value}'`); return false; - } - return typeof value === type; - } - if (type === 'string' || type === 'boolean') { - return typeof value === type; } + //if (value === null) { + // /** @todo Add unit-tests/asserts tests to make sure this never happens */ + // console.warn('type !== null already, so this can only be false'); + // return false; + //} else if (type[0] === '"' && type[type.length - 1] === '"') { const typeSlice = type.slice(1, -1); return value === typeSlice; - } - if (type[0] === "'" && type[type.length - 1] === "'") { + } else if (type[0] === "'" && type[type.length - 1] === "'") { const typeSlice = type.slice(1, -1); return value === typeSlice; - } - /** @todo Callback is PC specific, parse JSDoc callback types like typedefs */ - if (type === 'Function' || type === 'function' || type.includes('Callback')) { - return typeof value === "function"; - } - if (type === 'ObjectConstructor') { - return typeof value.constructor === 'function'; - } - if (type === 'class') { - if (value && expect.elementType === 'ScriptType') { - if (value.name === 'scriptType') { - return true; - } - const proto = Object.getPrototypeOf(value); - if (proto?.name === 'ScriptType') { - return true; - } - } - warn(`${loc}> validateType> class> expected object, not '${value}'`); - return false; - } - if (type === 'ResourceHandler') { - return value?.constructor?.name.endsWith('Handler'); - } - // Camera, Float32Array etc. - if (value && value.constructor && value.constructor.name === type) { + } else if (value && value.constructor && value.constructor.name === type) { + // Camera, Float32Array etc. return true; + } else if (classes[type]) { + // Inheritance check, allow Application for AppBase, allow Entity for GraphNode etc. + return value instanceof classes[type]; } if (typeof window !== 'undefined') { const windowClass = window[type]; @@ -215,11 +201,7 @@ function validateType(value, expect, loc, name, critical = true, warn, depth) { } } } - // inheritance check, allow Application for AppBase, allow Entity for GraphNode etc. - if (classes[type]) { - return value instanceof classes[type]; - } - warn("unchecked", {value, type, loc, name}); + warn('unchecked', {value, type, loc, name}); return false; } export {validateType}; diff --git a/test/typechecking.json b/test/typechecking.json index b3358cd..73c2e69 100644 --- a/test/typechecking.json +++ b/test/typechecking.json @@ -123,6 +123,10 @@ "input": "./test/typechecking/tuple-named-input.mjs", "output": "./test/typechecking/tuple-named-output.mjs" }, + { + "input": "./test/typechecking/type-new-input.mjs", + "output": "./test/typechecking/type-new-output.mjs" + }, { "input": "./test/typechecking/typedef-BigIntKeyword-input.mjs", "output": "./test/typechecking/typedef-BigIntKeyword-output.mjs" diff --git a/test/typechecking/type-new-input.mjs b/test/typechecking/type-new-input.mjs new file mode 100644 index 0000000..f69f314 --- /dev/null +++ b/test/typechecking/type-new-input.mjs @@ -0,0 +1,10 @@ +class ObjectPool { + /** + * @param {new (...args: any[]) => any} constructorFunc - The constructor function for the + * objects in the pool. + */ + constructor(constructorFunc) { + this._constructor = constructorFunc; + } +} +const objectPool = new ObjectPool(Float32Array); diff --git a/test/typechecking/type-new-output.mjs b/test/typechecking/type-new-output.mjs new file mode 100644 index 0000000..8ede682 --- /dev/null +++ b/test/typechecking/type-new-output.mjs @@ -0,0 +1,30 @@ +class ObjectPool { + /** + * @param {new (...args: any[]) => any} constructorFunc - The constructor function for the + * objects in the pool. + */ + constructor(constructorFunc) { + if (!inspectType(constructorFunc, { + "type": "new", + "parameters": [ + { + "type": "array", + "elementType": { + "type": { + "type": "array", + "elementType": "any" + }, + "name": "args" + } + } + ], + "ret": "any", + "optional": false + }, 'ObjectPool#constructor', 'constructorFunc')) { + youCanAddABreakpointHere(); + } + this._constructor = constructorFunc; + } +} +registerClass(ObjectPool); +const objectPool = new ObjectPool(Float32Array);