Skip to content

Commit 76cf1f5

Browse files
authored
add --strict option (#19)
* add --strict option * add test case for non strict mode * add docstring for source name counter
1 parent 1a1865f commit 76cf1f5

File tree

5 files changed

+37
-5
lines changed

5 files changed

+37
-5
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,10 @@ TypeScript2Python supports many of TypeScripts type constructs, including:
9595
9696
## Transpiler options
9797
98+
### Strict
99+
100+
Use the `--strict` flag to enable all strict type-checking options to ensure `undefined` and `null` properties are not ignored during transpilation.
101+
98102
### Nullable optionals
99103
100104
In TypeScript objects, optional values can also be set to `undefined`. By default we assume the according Python

src/index.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@ import path from "path";
55
import { program } from "@commander-js/extra-typings";
66
import { typeScriptToPython } from "./typeScriptToPython";
77
import { Ts2PyConfig } from "./config";
8+
import { readFileSync } from "fs";
89

9-
const compile = (fileNames: string[], config: Ts2PyConfig) => {
10+
const compile = (fileNames: string[], config: Ts2PyConfig & {strict?: boolean}) => {
1011
const program = ts.createProgram(fileNames, {
1112
noEmit: true,
1213
allowJs: true,
1314
resolveJsonModule: true,
1415
skipLibCheck: true,
16+
strict: config.strict,
1517
});
1618

1719
const relevantSourceFiles = program
@@ -21,7 +23,6 @@ const compile = (fileNames: string[], config: Ts2PyConfig) => {
2123
.map((fn) => path.relative(fn, f.fileName) === "")
2224
.reduce((a, b) => a || b),
2325
);
24-
2526
const transpiled = typeScriptToPython(program.getTypeChecker(), relevantSourceFiles, config)
2627
console.log(transpiled);
2728
}
@@ -30,6 +31,7 @@ program
3031
.name("typescript2python")
3132
.description("A program that converts TypeScript type definitions to Python")
3233
.option("--nullable-optionals", "if set, optional entries in dictionaries will be nullable, e.g. `NotRequired[Optional[T]]`")
34+
.option("--strict", "Enable all strict type-checking options.")
3335
.arguments("<input...>")
3436
.action((args, options) => {
3537
compile(args, options)

src/testing/basic.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,30 @@ describe("transpiling basic types", () => {
5757
"export type T = number | string | Record<string, boolean>",
5858
"from typing_extensions import Dict, Union\n\nT = Union[str,float,Dict[str,bool]]",
5959
],
60+
[
61+
"export type T = number | undefined",
62+
// without strict mode the `undefined` gets lost here
63+
"T = float",
64+
],
6065
])("transpiles %p to %p", async (input, expected) => {
6166
const result = await transpileString(input);
6267
expect(result).toEqual(expected);
6368
});
6469

70+
it.each([
71+
[
72+
"export type T = number | undefined",
73+
"from typing_extensions import Union\n\nT = Union[None,float]",
74+
],
75+
[
76+
"export type T = number | null",
77+
"from typing_extensions import Union\n\nT = Union[None,float]",
78+
],
79+
])("transpiles %p to %p when strict", async (input, expected) => {
80+
const result = await transpileString(input, {}, { strict: true });
81+
expect(result).toEqual(expected);
82+
});
83+
6584
it("only transpiles exported types", async () => {
6685
const result = await transpileString(`
6786
type NotExported = number;

src/testing/dicts.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ class A(TypedDict):
6464
expect(result).toContain(`class A(TypedDict):\n foo: NotRequired[str]`);
6565
});
6666

67-
it("transpiles optional values with non-null optionals as NotRequired[T]", async () => {
67+
it.only("transpiles optional values with non-null optionals as NotRequired[T]", async () => {
6868
const result = await transpileString(`export type A = { foo?: string }`, {
6969
nullableOptionals: true,
7070
});

src/testing/utils.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,13 @@ import { createProject, ts } from "@ts-morph/bootstrap";
88
**/
99
let globalProject: ReturnType<typeof createProject> | undefined;
1010

11+
/** Each file should get a unique name to avoid issues. */
12+
let i = 0;
13+
1114
export const transpileString = async (
1215
code: string,
1316
config: Ts2PyConfig = {},
17+
compilerOptions: ts.CompilerOptions = {},
1418
) => {
1519
if (globalProject === undefined) {
1620
globalProject = createProject({
@@ -19,11 +23,14 @@ export const transpileString = async (
1923
}
2024

2125
const project = await globalProject;
22-
const fileName = `source.ts`;
26+
const fileName = `source_${i++}.ts`;
2327

2428
// instead of adding a new source file for each program, we update the existing one.
2529
const sourceFile = project.updateSourceFile(fileName, code);
26-
const program = project.createProgram();
30+
const program = project.createProgram({
31+
rootNames: [fileName],
32+
options: { ...project.compilerOptions, ...compilerOptions },
33+
});
2734
const diagnostics = ts.getPreEmitDiagnostics(program);
2835

2936
if (diagnostics.length > 0) {

0 commit comments

Comments
 (0)