Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: support intrinsic string manipulation types (#1173)
- Loading branch information
Showing
11 changed files
with
216 additions
and
23 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
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,39 @@ | ||
import ts from "typescript"; | ||
import { Context } from "../NodeParser"; | ||
import { SubNodeParser } from "../SubNodeParser"; | ||
import { BaseType } from "../Type/BaseType"; | ||
import { LiteralType } from "../Type/LiteralType"; | ||
import { UnionType } from "../Type/UnionType"; | ||
import assert from "../Utils/assert"; | ||
import { extractLiterals } from "../Utils/extractLiterals"; | ||
|
||
export const intrinsicMethods: Record<string, ((v: string) => string) | undefined> = { | ||
Uppercase: (v) => v.toUpperCase(), | ||
Lowercase: (v) => v.toLowerCase(), | ||
Capitalize: (v) => v[0].toUpperCase() + v.slice(1), | ||
Uncapitalize: (v) => v[0].toLowerCase() + v.slice(1), | ||
}; | ||
|
||
export class IntrinsicNodeParser implements SubNodeParser { | ||
public supportsNode(node: ts.KeywordTypeNode): boolean { | ||
return node.kind === ts.SyntaxKind.IntrinsicKeyword; | ||
} | ||
public createType(node: ts.KeywordTypeNode, context: Context): BaseType | undefined { | ||
const methodName = getParentName(node); | ||
const method = intrinsicMethods[methodName]; | ||
assert(method, `Unknown intrinsic method: ${methodName}`); | ||
const literals = extractLiterals(context.getArguments()[0]) | ||
.map(method) | ||
.map((literal) => new LiteralType(literal)); | ||
if (literals.length === 1) { | ||
return literals[0]; | ||
} | ||
return new UnionType(literals); | ||
} | ||
} | ||
|
||
function getParentName(node: ts.KeywordTypeNode): string { | ||
const parent = node.parent; | ||
assert(ts.isTypeAliasDeclaration(parent), "Only intrinsics part of a TypeAliasDeclaration are supported."); | ||
return parent.name.text; | ||
} |
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,7 @@ | ||
import { LogicError } from "../Error/LogicError"; | ||
|
||
export default function assert(value: unknown, message: string): asserts value { | ||
if (!value) { | ||
throw new LogicError(message); | ||
} | ||
} |
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,31 @@ | ||
import { UnknownTypeError } from "../Error/UnknownTypeError"; | ||
import { AliasType } from "../Type/AliasType"; | ||
import { BaseType } from "../Type/BaseType"; | ||
import { LiteralType } from "../Type/LiteralType"; | ||
import { UnionType } from "../Type/UnionType"; | ||
|
||
function* _extractLiterals(type: BaseType | undefined): Iterable<string> { | ||
if (!type) { | ||
return; | ||
} | ||
if (type instanceof LiteralType) { | ||
yield type.getValue().toString(); | ||
return; | ||
} | ||
if (type instanceof UnionType) { | ||
for (const t of type.getTypes()) { | ||
yield* _extractLiterals(t); | ||
} | ||
return; | ||
} | ||
if (type instanceof AliasType) { | ||
yield* _extractLiterals(type.getType()); | ||
return; | ||
} | ||
|
||
throw new UnknownTypeError(type); | ||
} | ||
|
||
export function extractLiterals(type: BaseType | undefined): string[] { | ||
return [..._extractLiterals(type)]; | ||
} |
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 { LogicError } from "../../src/Error/LogicError"; | ||
import assert from "../../src/Utils/assert"; | ||
|
||
describe("validate assert", () => { | ||
it.each` | ||
value | ||
${"hello"} | ||
${1} | ||
${true} | ||
${{}} | ||
`("success $value", ({ value }) => { | ||
expect(() => assert(value, "message")).not.toThrow(); | ||
}); | ||
|
||
it.each` | ||
value | ||
${""} | ||
${0} | ||
${false} | ||
${undefined} | ||
${null} | ||
`("fail $value", ({ value }) => { | ||
expect(() => assert(value, "failed to be true")).toThrowError(LogicError); | ||
}); | ||
}); |
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,14 @@ | ||
type Abort = "abort"; | ||
type Result = "ok" | "fail" | Uppercase<Abort> | "Success"; | ||
type ResultUpper = Uppercase<Result>; | ||
type ResultLower = Lowercase<ResultUpper>; | ||
type ResultCapitalize = Capitalize<Result>; | ||
type ResultUncapitalize = Uncapitalize<ResultCapitalize>; | ||
|
||
export interface MyObject { | ||
result: Result; | ||
resultUpper: ResultUpper; | ||
resultLower: ResultLower; | ||
resultCapitalize: ResultCapitalize; | ||
resultUncapitalize: ResultUncapitalize; | ||
} |
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,64 @@ | ||
{ | ||
"$ref": "#/definitions/MyObject", | ||
"$schema": "http://json-schema.org/draft-07/schema#", | ||
"definitions": { | ||
"MyObject": { | ||
"additionalProperties": false, | ||
"properties": { | ||
"result": { | ||
"enum": [ | ||
"ok", | ||
"fail", | ||
"ABORT", | ||
"Success" | ||
], | ||
"type": "string" | ||
}, | ||
"resultCapitalize": { | ||
"enum": [ | ||
"Ok", | ||
"Fail", | ||
"ABORT", | ||
"Success" | ||
], | ||
"type": "string" | ||
}, | ||
"resultLower": { | ||
"enum": [ | ||
"ok", | ||
"fail", | ||
"abort", | ||
"success" | ||
], | ||
"type": "string" | ||
}, | ||
"resultUncapitalize": { | ||
"enum": [ | ||
"ok", | ||
"fail", | ||
"aBORT", | ||
"success" | ||
], | ||
"type": "string" | ||
}, | ||
"resultUpper": { | ||
"enum": [ | ||
"OK", | ||
"FAIL", | ||
"ABORT", | ||
"SUCCESS" | ||
], | ||
"type": "string" | ||
} | ||
}, | ||
"required": [ | ||
"result", | ||
"resultUpper", | ||
"resultLower", | ||
"resultCapitalize", | ||
"resultUncapitalize" | ||
], | ||
"type": "object" | ||
} | ||
} | ||
} |