-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
createType
: support indexed access (#122)
* Fix validateMapping and add test * Work out type indexedAccess for getTypeKeys * Add src-runtime/resolveType.js (with spec) * add more resolveType tests * Add createTypeFromKeyof * Fix createTypeFromKeyof stuff * Fix registerTypedef types * Add src-runtime/createType.js and src-runtime/createTypeFromIndexedAccess.js with some repl/spec files Other fixes * Add src-runtime/validateIndexedAccess.js (only for `IndexedAccess` typedef right now) * validate-mapping-indexed-access.mjs - just some special case typedef for testing * Fix lint
- Loading branch information
1 parent
85de72c
commit 8213566
Showing
20 changed files
with
430 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
*.repl.js |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import {resolveType } from "./resolveType.js"; | ||
import {createTypeFromIndexedAccess} from "./createTypeFromIndexedAccess.js"; | ||
import {createTypeFromKeyof } from "./createTypeFromKeyof.js"; | ||
import {createTypeFromMapping } from "./createTypeFromMapping.js"; | ||
/** | ||
* @param {string|import('./validateType.mjs').Type} expect - The supposed type information of said value. | ||
* @param {console["warn"]} warn - Function to warn with. | ||
* @returns {import('./validateType.mjs').Type|undefined} - New type that can be used for validatoin | ||
*/ | ||
function createType(expect, warn) { | ||
const mapping = resolveType(expect, 'mapping', warn); | ||
if (mapping) { | ||
return createTypeFromMapping(mapping, warn); | ||
} | ||
const indexedAccess = resolveType(expect, 'indexedAccess', warn); | ||
if (indexedAccess) { | ||
return createTypeFromIndexedAccess(indexedAccess, warn); | ||
} | ||
const keyof = resolveType(expect, 'keyof', warn); | ||
if (keyof) { | ||
return createTypeFromKeyof(keyof, warn); | ||
} | ||
return expect; | ||
} | ||
export {createType}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import {createType } from './createType.js'; | ||
import {registerTypedef} from './registerTypedef.mjs'; | ||
function prepare() { | ||
/** | ||
* @typedef {{a: 'aa', b: 'bb', c: 'cc'}} Obj | ||
* @typedef {Obj[keyof Obj]} ObjValues | ||
* @typedef {ObjValues} ObjValuesTypedef | ||
* @typedef {ObjValuesTypedef} ObjValuesTypedef2 | ||
*/ | ||
registerTypedef('Obj', { | ||
"type": "object", | ||
"properties": { | ||
"a": "'aa'", | ||
"b": "\"bb\"", | ||
"c": "'cc'" | ||
} | ||
}); | ||
registerTypedef('ObjValues', { | ||
"type": "indexedAccess", | ||
"index": { | ||
"type": "keyof", | ||
"argument": "Obj" | ||
}, | ||
"object": "Obj" | ||
}); | ||
registerTypedef('ObjValuesTypedef', "ObjValues"); | ||
registerTypedef('ObjValuesTypedef2', "ObjValuesTypedef"); | ||
} | ||
function test1() { | ||
prepare(); | ||
/** @type {import('./validateUnion.mjs').Union} */ | ||
// @ts-ignore | ||
const t = createType("ObjValuesTypedef2", console.warn); | ||
if (!t) { | ||
console.warn('createType: t is not defined.'); | ||
return false; | ||
} | ||
if (t.type !== 'union') { | ||
console.warn('t is not an union.', t); | ||
return false; | ||
} | ||
if (t.members.length !== 3) { | ||
console.warn('t should have three union members.'); | ||
return false; | ||
} | ||
if (t.members[0] !== 'aa') { | ||
console.warn(`Value for t.members[0] should be 'aa', but got '${t.members[0]}'.`); | ||
return false; | ||
} | ||
if (t.members[1] !== 'bb') { | ||
console.warn(`Value for t.members[1] should be 'aa', but got '${t.members[1]}'.`); | ||
return false; | ||
} | ||
if (t.members[2] !== 'cc') { | ||
console.warn(`Value for t.members[2] should be 'aa', but got '${t.members[2]}'.`); | ||
return false; | ||
} | ||
return true; | ||
} | ||
export const tests = [ | ||
test1, | ||
]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
//import {createType } from "./createType.js"; | ||
import {resolveType } from "./resolveType.js"; | ||
//import {replaceType } from "./replaceType.js"; | ||
import {getTypeKeys } from "./validateKeyof.mjs"; | ||
/** | ||
* @param {import('./validateIndexedAccess.js').IndexedAccess} expect - The supposed type information of said value. | ||
* @param {console["warn"]} warn - Function to warn with. | ||
* @returns {import('./validateType.mjs').TypeObject|undefined} - New type that can be used for validation. | ||
*/ | ||
function createTypeFromIndexedAccess(expect, warn) { | ||
const {object, index} = expect; | ||
const resolvedObject = resolveType(object, 'object', warn); | ||
if (resolvedObject) { | ||
// const indexType = createType(index, warn); | ||
const indexKeys = getTypeKeys(index, warn); | ||
//console.log("createTypeFromIndexedAccess", {resolvedObject, object, index, indexType, indexKeys}); | ||
if (!indexKeys) { | ||
warn('createTypeFromIndexedAccess: missing indexKeys'); | ||
return; | ||
} | ||
/** @type {import('./validateType.mjs').Type[]} */ | ||
const members = []; | ||
for (const indexKey of indexKeys) { | ||
let prop = resolvedObject.properties[indexKey]; | ||
if (!prop) { | ||
console.warn(`Missing prop for ${indexKey}`, {indexKey}); | ||
} | ||
//console.log("asd", indexKey, prop); | ||
if (typeof prop === 'string') { | ||
// Remove ' from string literal | ||
if (prop[0] === "'" && prop[prop.length - 1] === "'") { | ||
prop = prop.slice(1, -1); | ||
} | ||
// Remove " from string literal | ||
if (prop[0] === '"' && prop[prop.length - 1] === '"') { | ||
prop = prop.slice(1, -1); | ||
} | ||
} | ||
members.push(prop); | ||
//const cloneResult = structuredClone(result); | ||
//replaceType(cloneResult, element, typeKey, warn); | ||
//properties[typeKey] = cloneResult; | ||
} | ||
return {type: 'union', members, optional: false}; | ||
} | ||
} | ||
export {createTypeFromIndexedAccess}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
/** | ||
* @typedef {{a: 'aa', b: "bb", c: 'cc'}} Obj | ||
* @typedef {{a: 0, b: '', c: undefined, d: null}} ObjTestcase | ||
* @typedef {Obj[keyof Obj]} ObjValues | ||
* @typedef {ObjTestcase[keyof ObjTestcase]} ObjValuesTestcase - Should be "" | 0 | null | undefined | ||
*/ | ||
const t = createType('ObjValues', console.log); | ||
console.log(t); | ||
/** | ||
* @param {ObjTest1} x - First argument. | ||
* @returns {ObjTest1} - Return value. | ||
*/ | ||
function identity1(x) { | ||
return x; | ||
} | ||
identity1({ | ||
aa: {testkey: 'aa'}, | ||
bb: {testkey: 'bb'}, | ||
cc: {testkey: 'cc'}, | ||
}); | ||
/** | ||
* @param {ObjTest2} x - First argument. | ||
* @returns {ObjTest2} - Return value. | ||
*/ | ||
function identity2(x) { | ||
return x; | ||
} | ||
identity2({ | ||
aa: {testkey: 'aa'}, | ||
bb: {testkey: 'bb'}, | ||
cc: {testkey: 'cc'}, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import {resolveType} from "./resolveType.js"; | ||
/** | ||
* @todo reuse this function in createTypeFromMapping/getTypeKeys | ||
* @param {import('./validateKeyof.mjs').Keyof} expect - The supposed type information of said value. | ||
* @param {console["warn"]} warn - Function to warn with. | ||
* @returns {import('./validateType.mjs').TypeObject|undefined} - New type that can be used for validatoin | ||
*/ | ||
function createTypeFromKeyof(expect, warn) { | ||
const {argument} = expect; | ||
const object = resolveType(argument, 'object', warn); | ||
if (object) { | ||
const optional = false; | ||
const members = Object.keys(object.properties); | ||
return {type: 'union', optional, members}; | ||
} | ||
warn('createTypeFromKeyof: unhandled type.', {expect}); | ||
} | ||
export {createTypeFromKeyof}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
/** | ||
* @typedef {{a: 'aa', b: 'bb', c: 'cc'}} Obj | ||
* @typedef {keyof Obj} ObjKeys | ||
* @typedef {ObjKeys} ObjKeysTypedef | ||
*/ | ||
console.log('Obj', typedefs.Obj); | ||
console.log('ObjKeys', typedefs.ObjKeys); | ||
const newType = createType("ObjKeysTypedef", console.warn); | ||
console.log('newType', newType); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import {createType } from './createType.js'; | ||
import {registerTypedef} from './registerTypedef.mjs'; | ||
function prepare() { | ||
/** | ||
* @template T | ||
* @typedef {{[K in keyof T]: T[K] extends object ? Unpack<T[K]> : T[K]}} Unpack | ||
*/ | ||
/** | ||
* @typedef {{a: 'aa', b: 'bb', c: 'cc'}} Obj | ||
* @typedef {keyof Obj} ObjKeys | ||
* @typedef {ObjKeys} ObjKeysTypedef | ||
* @typedef {Unpack<ObjKeys>} ObjKeysUnpacked | ||
*/ | ||
registerTypedef('Obj', { | ||
"type": "object", | ||
"properties": { | ||
"a": "'aa'", | ||
"b": "'bb'", | ||
"c": "'cc'" | ||
} | ||
}); | ||
registerTypedef('ObjKeys', { | ||
"type": "keyof", | ||
"argument": "Obj" | ||
}); | ||
registerTypedef('ObjKeysTypedef', "ObjKeys"); | ||
} | ||
function test1() { | ||
prepare(); | ||
/** @type {import('./validateUnion.mjs').Union} */ | ||
const newType = createType('ObjKeysTypedef', console.warn); | ||
if (!newType) { | ||
console.warn('t is not defined.'); | ||
return false; | ||
} | ||
const {type, members} = newType; | ||
if (type !== 'union') { | ||
console.warn('t is not an union.'); | ||
return false; | ||
} | ||
if (members.length !== 3) { | ||
console.warn('t should have three union members.'); | ||
return false; | ||
} | ||
if (members[0] !== 'a') { | ||
console.warn("t.elements[0] should be 'a'"); | ||
return false; | ||
} | ||
if (members[1] !== 'b') { | ||
console.warn("t.elements[1] should be 'b'"); | ||
return false; | ||
} | ||
if (members[2] !== 'c') { | ||
console.warn("t.elements[2] should be 'c'"); | ||
return false; | ||
} | ||
return true; | ||
} | ||
export const tests = [ | ||
test1, | ||
]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import {typedefs} from "./registerTypedef.mjs"; | ||
/** | ||
* @param {any} type - The type. | ||
* @param {string} as - What to resolve as. | ||
* @param {console["warn"]} warn - Function to warn with. | ||
* @returns {any} - Resolved type. | ||
*/ | ||
function resolveType(type, as, warn) { | ||
for (let depth = 0; depth < 20; depth++) { | ||
if (typeof type === 'string' && typedefs[type]) { | ||
type = typedefs[type]; | ||
} | ||
if (type.type) { | ||
if (type.type === as) { | ||
return type; | ||
} | ||
// If we already resolved as a structured type, it can't be a typedef any longer. | ||
return; | ||
} | ||
} | ||
warn('resolveType: exceeded allowed depth'); | ||
} | ||
export {resolveType}; |
Oops, something went wrong.