diff --git a/src/generator/typescript.cc b/src/generator/typescript.cc index 75e4d5f..1bbc6d4 100644 --- a/src/generator/typescript.cc +++ b/src/generator/typescript.cc @@ -1,5 +1,6 @@ #include +#include // std::hex, std::setfill, std::setw #include // std::ostringstream namespace { @@ -15,6 +16,12 @@ auto escape_string(const std::string &input) -> std::string { case '"': result << "\\\""; break; + case '\b': + result << "\\b"; + break; + case '\f': + result << "\\f"; + break; case '\n': result << "\\n"; break; @@ -25,7 +32,13 @@ auto escape_string(const std::string &input) -> std::string { result << "\\t"; break; default: - result << character; + // Escape other control characters (< 0x20) using \uXXXX format + if (static_cast(character) < 0x20) { + result << "\\u" << std::hex << std::setfill('0') << std::setw(4) + << static_cast(static_cast(character)); + } else { + result << character; + } break; } } diff --git a/test/e2e/typescript/2020-12/control_characters_in_property_names/expected.d.ts b/test/e2e/typescript/2020-12/control_characters_in_property_names/expected.d.ts new file mode 100644 index 0000000..101d108 --- /dev/null +++ b/test/e2e/typescript/2020-12/control_characters_in_property_names/expected.d.ts @@ -0,0 +1,22 @@ +export type TestWithus = string; + +export type TestWithformfeed = string; + +export type TestWithbackspace = string; + +export type TestWithsoh = string; + +export type TestWithnull = string; + +export type TestNormal = string; + +export type TestAdditionalProperties = never; + +export interface Test { + "normal"?: TestNormal; + "with\bbackspace"?: TestWithbackspace; + "with\fformfeed"?: TestWithformfeed; + "with\u0000null"?: TestWithnull; + "with\u0001soh"?: TestWithsoh; + "with\u001fus"?: TestWithus; +} diff --git a/test/e2e/typescript/2020-12/control_characters_in_property_names/options.json b/test/e2e/typescript/2020-12/control_characters_in_property_names/options.json new file mode 100644 index 0000000..68213dc --- /dev/null +++ b/test/e2e/typescript/2020-12/control_characters_in_property_names/options.json @@ -0,0 +1,3 @@ +{ + "defaultPrefix": "Test" +} diff --git a/test/e2e/typescript/2020-12/control_characters_in_property_names/schema.json b/test/e2e/typescript/2020-12/control_characters_in_property_names/schema.json new file mode 100644 index 0000000..2295877 --- /dev/null +++ b/test/e2e/typescript/2020-12/control_characters_in_property_names/schema.json @@ -0,0 +1,13 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "normal": { "type": "string" }, + "with\bbackspace": { "type": "string" }, + "with\fformfeed": { "type": "string" }, + "with\u0000null": { "type": "string" }, + "with\u0001soh": { "type": "string" }, + "with\u001fus": { "type": "string" } + }, + "additionalProperties": false +} diff --git a/test/e2e/typescript/2020-12/control_characters_in_property_names/test.ts b/test/e2e/typescript/2020-12/control_characters_in_property_names/test.ts new file mode 100644 index 0000000..aaed97f --- /dev/null +++ b/test/e2e/typescript/2020-12/control_characters_in_property_names/test.ts @@ -0,0 +1,32 @@ +import { Test } from "./expected"; + +// Valid: object with control characters in property names +const validObject: Test = { + "normal": "hello", + "with\bbackspace": "value1", + "with\fformfeed": "value2", + "with\u0000null": "value3", + "with\u0001soh": "value4", + "with\u001fus": "value5" +}; + +// Valid: empty object (all properties are optional) +const emptyObject: Test = {}; + +// Valid: partial object +const partialObject: Test = { + "normal": "just normal" +}; + +// Invalid: extra property not allowed +const extraProp: Test = { + "normal": "hello", + // @ts-expect-error - extra property not allowed + "extra": "not allowed" +}; + +// Invalid: wrong type for property +const wrongType: Test = { + // @ts-expect-error - must be string + "normal": 123 +};