From 762064c8cdcd5267cb1f2da000f69524680a4dab Mon Sep 17 00:00:00 2001 From: Richard Simpson Date: Wed, 1 Mar 2023 00:52:58 -0600 Subject: [PATCH 1/3] feat: add support for unknown in typescript Adds support for `unknown` (and it's flow equivalent) in TypeScript. Closes #1619 --- .../src/language/TypeScriptFlow.ts | 53 +++++++++++++++---- 1 file changed, 43 insertions(+), 10 deletions(-) diff --git a/packages/quicktype-core/src/language/TypeScriptFlow.ts b/packages/quicktype-core/src/language/TypeScriptFlow.ts index 6f0b725d6..8a256fbb9 100644 --- a/packages/quicktype-core/src/language/TypeScriptFlow.ts +++ b/packages/quicktype-core/src/language/TypeScriptFlow.ts @@ -22,7 +22,8 @@ export const tsFlowOptions = Object.assign({}, javaScriptOptions, { nicePropertyNames: new BooleanOption("nice-property-names", "Transform property names to be JavaScripty", false), declareUnions: new BooleanOption("explicit-unions", "Explicitly name unions", false), preferUnions: new BooleanOption("prefer-unions", "Use union type instead of enum", false), - preferTypes: new BooleanOption("prefer-types", "Use types instead of interfaces", false) + preferTypes: new BooleanOption("prefer-types", "Use types instead of interfaces", false), + preferUnknown: new BooleanOption("prefer-unknown", "Use unknown instead of any type", false), }); const tsFlowTypeAnnotations = { @@ -46,7 +47,8 @@ export abstract class TypeScriptFlowBaseTargetLanguage extends JavaScriptTargetL tsFlowOptions.converters, tsFlowOptions.rawType, tsFlowOptions.preferUnions, - tsFlowOptions.preferTypes + tsFlowOptions.preferTypes, + tsFlowOptions.preferUnknown ]; } @@ -106,6 +108,8 @@ export abstract class TypeScriptFlowBaseRenderer extends JavaScriptRenderer { return super.namerForObjectProperty(); } } + + protected anyType(): string; protected sourceFor(t: Type): MultiWord { if (["class", "object", "enum"].indexOf(t.kind) >= 0) { @@ -113,7 +117,7 @@ export abstract class TypeScriptFlowBaseRenderer extends JavaScriptRenderer { } return matchType( t, - _anyType => singleWord("any"), + _anyType => singleWord(this.anyType()), _nullType => singleWord("null"), _boolType => singleWord("boolean"), _integerType => singleWord("number"), @@ -200,13 +204,13 @@ export abstract class TypeScriptFlowBaseRenderer extends JavaScriptRenderer { } protected deserializerFunctionLine(t: Type, name: Name): Sourcelike { - const jsonType = this._tsFlowOptions.rawType === "json" ? "string" : "any"; + const jsonType = this._tsFlowOptions.rawType === "json" ? "string" : this.anyType(); return ["function to", name, "(json: ", jsonType, "): ", this.sourceFor(t).source]; } protected serializerFunctionLine(t: Type, name: Name): Sourcelike { const camelCaseName = modifySource(camelCase, name); - const returnType = this._tsFlowOptions.rawType === "json" ? "string" : "any"; + const returnType = this._tsFlowOptions.rawType === "json" ? "string" : this.anyType(); return ["function ", camelCaseName, "ToJson(value: ", this.sourceFor(t).source, "): ", returnType]; } @@ -215,7 +219,8 @@ export abstract class TypeScriptFlowBaseRenderer extends JavaScriptRenderer { } protected get castFunctionLines(): [string, string] { - return ["function cast(val: any, typ: any): T", "function uncast(val: T, typ: any): any"]; + const any = this.anyType(); + return [`function cast(val: ${any}, typ: ${any}): T`, `function uncast(val: T, typ: ${any}): ${any}`]; } protected get typeAnnotations(): JavaScriptTypeAnnotations { @@ -247,13 +252,13 @@ export class TypeScriptRenderer extends TypeScriptFlowBaseRenderer { } protected deserializerFunctionLine(t: Type, name: Name): Sourcelike { - const jsonType = this._tsFlowOptions.rawType === "json" ? "string" : "any"; + const jsonType = this._tsFlowOptions.rawType === "json" ? "string" : this.anyType(); return ["public static to", name, "(json: ", jsonType, "): ", this.sourceFor(t).source]; } protected serializerFunctionLine(t: Type, name: Name): Sourcelike { const camelCaseName = modifySource(camelCase, name); - const returnType = this._tsFlowOptions.rawType === "json" ? "string" : "any"; + const returnType = this._tsFlowOptions.rawType === "json" ? "string" : this.anyType(); return ["public static ", camelCaseName, "ToJson(value: ", this.sourceFor(t).source, "): ", returnType]; } @@ -262,7 +267,13 @@ export class TypeScriptRenderer extends TypeScriptFlowBaseRenderer { } protected get typeAnnotations(): JavaScriptTypeAnnotations { - return Object.assign({ never: ": never" }, tsFlowTypeAnnotations); + const any = this.anyType(); + return Object.assign({ + any: `: ${any}`, + anyArray: ": ${any}[]", + anyMap: ": { [k: string]: ${any} }", + never: ": never" + }, tsFlowTypeAnnotations); } protected emitModuleExports(): void { @@ -314,6 +325,14 @@ export class TypeScriptRenderer extends TypeScriptFlowBaseRenderer { } ); } + + protected anyType(): string { + if (this._tsFlowOptions.preferUnknown) { + return "unknown"; + } else { + return "any"; + } + } } export class FlowTargetLanguage extends TypeScriptFlowBaseTargetLanguage { @@ -332,7 +351,13 @@ export class FlowRenderer extends TypeScriptFlowBaseRenderer { } protected get typeAnnotations(): JavaScriptTypeAnnotations { - return Object.assign({ never: "" }, tsFlowTypeAnnotations); + const any = this.anyType(); + return Object.assign({ + any: `: ${any}`, + anyArray: ": ${any}[]", + anyMap: ": { [k: string]: ${any} }", + never: "" + }, tsFlowTypeAnnotations); } protected emitEnum(e: EnumType, enumName: Name): void { @@ -363,4 +388,12 @@ export class FlowRenderer extends TypeScriptFlowBaseRenderer { this.ensureBlankLine(); super.emitSourceStructure(); } + + protected anyType(): string { + if (this._tsFlowOptions.preferUnknown) { + return "mixed"; + } else { + return "any"; + } + } } From 1f2a3af7e4557afea59d72a7cbb85cc7a50cc6c0 Mon Sep 17 00:00:00 2001 From: Richard Simpson Date: Wed, 1 Mar 2023 01:03:21 -0600 Subject: [PATCH 2/3] fix template fail --- packages/quicktype-core/src/language/TypeScriptFlow.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/quicktype-core/src/language/TypeScriptFlow.ts b/packages/quicktype-core/src/language/TypeScriptFlow.ts index 8a256fbb9..4133958ac 100644 --- a/packages/quicktype-core/src/language/TypeScriptFlow.ts +++ b/packages/quicktype-core/src/language/TypeScriptFlow.ts @@ -270,8 +270,8 @@ export class TypeScriptRenderer extends TypeScriptFlowBaseRenderer { const any = this.anyType(); return Object.assign({ any: `: ${any}`, - anyArray: ": ${any}[]", - anyMap: ": { [k: string]: ${any} }", + anyArray: `: ${any}[]`, + anyMap: `: { [k: string]: ${any} }`, never: ": never" }, tsFlowTypeAnnotations); } @@ -354,8 +354,8 @@ export class FlowRenderer extends TypeScriptFlowBaseRenderer { const any = this.anyType(); return Object.assign({ any: `: ${any}`, - anyArray: ": ${any}[]", - anyMap: ": { [k: string]: ${any} }", + anyArray: `: ${any}[]`, + anyMap: `: { [k: string]: ${any} }`, never: "" }, tsFlowTypeAnnotations); } From e09a9cc8d34d211a9f766ca399a7803033daeaae Mon Sep 17 00:00:00 2001 From: Richard Simpson Date: Thu, 2 Mar 2023 00:24:46 +0000 Subject: [PATCH 3/3] add work so far on unknown --- .../src/language/TypeScriptFlow.ts | 18 +++++++++--------- test/languages.ts | 3 ++- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/packages/quicktype-core/src/language/TypeScriptFlow.ts b/packages/quicktype-core/src/language/TypeScriptFlow.ts index 4133958ac..b9c9df9be 100644 --- a/packages/quicktype-core/src/language/TypeScriptFlow.ts +++ b/packages/quicktype-core/src/language/TypeScriptFlow.ts @@ -23,7 +23,7 @@ export const tsFlowOptions = Object.assign({}, javaScriptOptions, { declareUnions: new BooleanOption("explicit-unions", "Explicitly name unions", false), preferUnions: new BooleanOption("prefer-unions", "Use union type instead of enum", false), preferTypes: new BooleanOption("prefer-types", "Use types instead of interfaces", false), - preferUnknown: new BooleanOption("prefer-unknown", "Use unknown instead of any type", false), + preferUnknown: new BooleanOption("prefer-unknown", "Use unknown instead of any type", false) }); const tsFlowTypeAnnotations = { @@ -108,8 +108,8 @@ export abstract class TypeScriptFlowBaseRenderer extends JavaScriptRenderer { return super.namerForObjectProperty(); } } - - protected anyType(): string; + + protected abstract anyType(): string; protected sourceFor(t: Type): MultiWord { if (["class", "object", "enum"].indexOf(t.kind) >= 0) { @@ -268,12 +268,12 @@ export class TypeScriptRenderer extends TypeScriptFlowBaseRenderer { protected get typeAnnotations(): JavaScriptTypeAnnotations { const any = this.anyType(); - return Object.assign({ + return Object.assign({}, tsFlowTypeAnnotations, { any: `: ${any}`, anyArray: `: ${any}[]`, anyMap: `: { [k: string]: ${any} }`, never: ": never" - }, tsFlowTypeAnnotations); + }); } protected emitModuleExports(): void { @@ -325,7 +325,7 @@ export class TypeScriptRenderer extends TypeScriptFlowBaseRenderer { } ); } - + protected anyType(): string { if (this._tsFlowOptions.preferUnknown) { return "unknown"; @@ -352,12 +352,12 @@ export class FlowRenderer extends TypeScriptFlowBaseRenderer { protected get typeAnnotations(): JavaScriptTypeAnnotations { const any = this.anyType(); - return Object.assign({ + return Object.assign({}, tsFlowTypeAnnotations, { any: `: ${any}`, anyArray: `: ${any}[]`, anyMap: `: { [k: string]: ${any} }`, never: "" - }, tsFlowTypeAnnotations); + }); } protected emitEnum(e: EnumType, enumName: Name): void { @@ -388,7 +388,7 @@ export class FlowRenderer extends TypeScriptFlowBaseRenderer { this.ensureBlankLine(); super.emitSourceStructure(); } - + protected anyType(): string { if (this._tsFlowOptions.preferUnknown) { return "mixed"; diff --git a/test/languages.ts b/test/languages.ts index e48e417d3..5f40b91db 100644 --- a/test/languages.ts +++ b/test/languages.ts @@ -698,7 +698,8 @@ export const TypeScriptLanguage: Language = { { "declare-unions": "true" }, ["pokedex.json", { "prefer-types": "true" }], { "acronym-style": "pascal" }, - { converters: "all-objects" } + { converters: "all-objects" }, + { "prefer-unknown": "true" }, ], sourceFiles: ["src/language/TypeScript.ts"] };