Skip to content

Commit bf6b307

Browse files
committed
feat: add --prefer-unknown option for TypeScript and Flow
Add a `--prefer-unknown` CLI flag that emits `unknown` instead of `any` in TypeScript output, and `mixed` instead of `any` in Flow output. This is a fresh implementation of the feature proposed in PR #2194 by @RichiCoder1, adapted to the current codebase structure where TypeScriptFlow was split into multiple files. Closes #1619
1 parent 4195020 commit bf6b307

5 files changed

Lines changed: 42 additions & 11 deletions

File tree

packages/quicktype-core/src/language/TypeScriptFlow/FlowRenderer.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,26 @@ import type { ClassType, EnumType } from "../../Type";
55
import type { JavaScriptTypeAnnotations } from "../JavaScript";
66

77
import { TypeScriptFlowBaseRenderer } from "./TypeScriptFlowBaseRenderer";
8-
import { tsFlowTypeAnnotations } from "./utils";
98

109
export class FlowRenderer extends TypeScriptFlowBaseRenderer {
10+
protected anyType(): string {
11+
return this._tsFlowOptions.preferUnknown ? "mixed" : "any";
12+
}
13+
1114
protected forbiddenNamesForGlobalNamespace(): string[] {
1215
return ["Class", "Date", "Object", "String", "Array", "JSON", "Error"];
1316
}
1417

1518
protected get typeAnnotations(): JavaScriptTypeAnnotations {
16-
return Object.assign({ never: "" }, tsFlowTypeAnnotations);
19+
const a = this.anyType();
20+
return Object.assign({ never: "" }, {
21+
any: `: ${a}`,
22+
anyArray: `: ${a}[]`,
23+
anyMap: `: { [k: string]: ${a} }`,
24+
string: ": string",
25+
stringArray: ": string[]",
26+
boolean: ": boolean",
27+
});
1728
}
1829

1930
protected emitEnum(e: EnumType, enumName: Name): void {

packages/quicktype-core/src/language/TypeScriptFlow/TypeScriptFlowBaseRenderer.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ export abstract class TypeScriptFlowBaseRenderer extends JavaScriptRenderer {
6464

6565
return matchType<MultiWord>(
6666
t,
67-
(_anyType) => singleWord("any"),
67+
(_anyType) => singleWord(this.anyType()),
6868
(_nullType) => singleWord("null"),
6969
(_boolType) => singleWord("boolean"),
7070
(_integerType) => singleWord("number"),
@@ -113,6 +113,8 @@ export abstract class TypeScriptFlowBaseRenderer extends JavaScriptRenderer {
113113
);
114114
}
115115

116+
protected abstract anyType(): string;
117+
116118
protected abstract emitEnum(e: EnumType, enumName: Name): void;
117119

118120
protected abstract emitClassBlock(c: ClassType, className: Name): void;
@@ -198,7 +200,7 @@ export abstract class TypeScriptFlowBaseRenderer extends JavaScriptRenderer {
198200

199201
protected deserializerFunctionLine(t: Type, name: Name): Sourcelike {
200202
const jsonType =
201-
this._tsFlowOptions.rawType === "json" ? "string" : "any";
203+
this._tsFlowOptions.rawType === "json" ? "string" : this.anyType();
202204
return [
203205
"function to",
204206
name,
@@ -212,7 +214,7 @@ export abstract class TypeScriptFlowBaseRenderer extends JavaScriptRenderer {
212214
protected serializerFunctionLine(t: Type, name: Name): Sourcelike {
213215
const camelCaseName = modifySource(camelCase, name);
214216
const returnType =
215-
this._tsFlowOptions.rawType === "json" ? "string" : "any";
217+
this._tsFlowOptions.rawType === "json" ? "string" : this.anyType();
216218
return [
217219
"function ",
218220
camelCaseName,
@@ -228,9 +230,10 @@ export abstract class TypeScriptFlowBaseRenderer extends JavaScriptRenderer {
228230
}
229231

230232
protected get castFunctionLines(): [string, string] {
233+
const a = this.anyType();
231234
return [
232-
"function cast<T>(val: any, typ: any): T",
233-
"function uncast<T>(val: T, typ: any): any",
235+
`function cast<T>(val: ${a}, typ: ${a}): T`,
236+
`function uncast<T>(val: T, typ: ${a}): ${a}`,
234237
];
235238
}
236239

packages/quicktype-core/src/language/TypeScriptFlow/TypeScriptRenderer.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,19 @@ import { isNamedType } from "../../Type/TypeUtils";
66
import type { JavaScriptTypeAnnotations } from "../JavaScript";
77

88
import { TypeScriptFlowBaseRenderer } from "./TypeScriptFlowBaseRenderer";
9-
import { tsFlowTypeAnnotations } from "./utils";
109

1110
export class TypeScriptRenderer extends TypeScriptFlowBaseRenderer {
11+
protected anyType(): string {
12+
return this._tsFlowOptions.preferUnknown ? "unknown" : "any";
13+
}
14+
1215
protected forbiddenNamesForGlobalNamespace(): string[] {
1316
return ["Array", "Date"];
1417
}
1518

1619
protected deserializerFunctionLine(t: Type, name: Name): Sourcelike {
1720
const jsonType =
18-
this._tsFlowOptions.rawType === "json" ? "string" : "any";
21+
this._tsFlowOptions.rawType === "json" ? "string" : this.anyType();
1922
return [
2023
"public static to",
2124
name,
@@ -29,7 +32,7 @@ export class TypeScriptRenderer extends TypeScriptFlowBaseRenderer {
2932
protected serializerFunctionLine(t: Type, name: Name): Sourcelike {
3033
const camelCaseName = modifySource(camelCase, name);
3134
const returnType =
32-
this._tsFlowOptions.rawType === "json" ? "string" : "any";
35+
this._tsFlowOptions.rawType === "json" ? "string" : this.anyType();
3336
return [
3437
"public static ",
3538
camelCaseName,
@@ -45,7 +48,15 @@ export class TypeScriptRenderer extends TypeScriptFlowBaseRenderer {
4548
}
4649

4750
protected get typeAnnotations(): JavaScriptTypeAnnotations {
48-
return Object.assign({ never: ": never" }, tsFlowTypeAnnotations);
51+
const a = this.anyType();
52+
return Object.assign({ never: ": never" }, {
53+
any: `: ${a}`,
54+
anyArray: `: ${a}[]`,
55+
anyMap: `: { [k: string]: ${a} }`,
56+
string: ": string",
57+
stringArray: ": string[]",
58+
boolean: ": boolean",
59+
});
4960
}
5061

5162
protected emitModuleExports(): void {

packages/quicktype-core/src/language/TypeScriptFlow/language.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@ export const tsFlowOptions = Object.assign({}, javaScriptOptions, {
4040
false,
4141
),
4242
readonly: new BooleanOption("readonly", "Use readonly type members", false),
43+
preferUnknown: new BooleanOption(
44+
"prefer-unknown",
45+
"Use unknown instead of any type",
46+
false,
47+
),
4348
});
4449

4550
export const typeScriptLanguageConfig = {

test/languages.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -839,6 +839,7 @@ export const TypeScriptLanguage: Language = {
839839
{ "acronym-style": "pascal" },
840840
{ converters: "all-objects" },
841841
{ readonly: "true" },
842+
{ "prefer-unknown": "true" },
842843
],
843844
sourceFiles: ["src/language/TypeScript/index.ts"],
844845
};

0 commit comments

Comments
 (0)