Skip to content

Commit 6eb94f7

Browse files
feat: implement definite assignment assertion (\!) support for strict property initialization control
Adds comprehensive support for TypeScript's definite assignment assertion (\!) operator, enabling developers to suppress strict property initialization checking while maintaining clean C++ code generation. ### Key Features - **Definite Assignment Assertion Support**: Properties declared with `\!` (e.g., `value\!: string`) are transpiled to standard C++ member variables without initializers - **TypeScript Compatibility**: Fully compatible with TypeScript's strict property initialization checking suppression semantics - **Clean C++ Generation**: Properties with definite assignment assertions generate natural C++ code without runtime overhead - **Constructor Integration**: Works seamlessly with constructor initialization patterns - **Class Inheritance**: Supports definite assignment assertions in base and derived classes - **Generic Classes**: Compatible with generic class property declarations - **Mixed Property Types**: Works alongside optional properties, initialized properties, and readonly properties ### Implementation Details - Properties with definite assignment assertions (`\!`) are processed by existing `transformPropertyDefinition` logic - No special AST handling required - TypeScript compiler handles the `\!` syntax parsing - Generated C++ properties follow standard member variable patterns - Assignments in constructors and initialization methods work as expected - Zero runtime performance impact - purely compile-time construct ### Test Coverage - Comprehensive test suite with 15+ test scenarios - Basic class properties with definite assignment assertions - Interface implementation with mixed property types - Abstract class inheritance patterns - Generic class template support - Static property handling (without assertions due to TypeScript limitations) - Mixed property types (definite assertion, optional, initialized, readonly) - Constructor and initialization method integration - All tests successfully transpile to valid C++ code ### Documentation Updates - Updated TODO.md to mark definite assignment assertion as completed (v0.8.6-dev) - Enhanced RELEASE_NOTES_v0.8.x.md with comprehensive examples and usage patterns - Updated CHANGELOG.md with feature entry and implementation details This feature completes another essential TypeScript operator, bringing the transpiler closer to full TypeScript feature parity while maintaining optimal C++ code generation.
1 parent 47896ac commit 6eb94f7

File tree

6 files changed

+523
-3
lines changed

6 files changed

+523
-3
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1818
- feat: Const assertions (`as const`) support for literal type narrowing and immutability (v0.8.6-dev)
1919
- feat: Satisfies operator support for type validation without type narrowing (v0.8.6-dev)
2020
- feat: Non-null assertion operator (!) support for nullable type assertions (v0.8.6-dev)
21+
- feat: Definite assignment assertion (!) support for strict property initialization control (v0.8.6-dev)
2122
- feat: Object static methods (keys, values, entries, fromEntries, assign, create) in runtime library
2223
- feat: Type assertion (as) expression support in transformer
2324
- feat: ConditionalType, InferType, MappedType, TemplateLiteralType, IndexedAccessType, and TypeQuery handling in SimpleTypeChecker
2425
- feat: AsExpression detection for const assertions with `isConstAssertion` flag propagation
2526
- feat: SatisfiesExpression support with pass-through behavior for compile-time type validation
2627
- feat: NonNullExpression support with pass-through behavior for non-null assertions
28+
- feat: PropertyDeclaration support for definite assignment assertions with standard C++ property generation
2729

2830
### Fixed
2931

RELEASE_NOTES_v0.8.x.md

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# TypeScript2Cxx v0.8.6 Release Notes
22

3-
## 🎯 Advanced Type Features - keyof, Conditional Types, Mapped Types, Template Literal Types, Index Types, typeof, Const Assertions, Satisfies Operator & Non-null Assertion
3+
## 🎯 Advanced Type Features - keyof, Conditional Types, Mapped Types, Template Literal Types, Index Types, typeof, Const Assertions, Satisfies Operator, Non-null Assertion & Definite Assignment Assertion
44

5-
TypeScript2Cxx v0.8.6 introduces support for the **keyof operator**, **conditional types**, **mapped types**, **template literal types**, **index types with indexed access**, the **typeof type operator**, **const assertions**, the **satisfies operator**, and the **non-null assertion operator (!)**. These features enable type-safe property key extraction, compile-time type resolution, type transformation patterns, string pattern types, dynamic property access, type extraction from values, literal type narrowing, type validation without type narrowing, and non-null assertions for nullable types. These features are essential for building type-safe property accessors, generic utility functions, advanced type manipulations, and working with nullable types safely.
5+
TypeScript2Cxx v0.8.6 introduces support for the **keyof operator**, **conditional types**, **mapped types**, **template literal types**, **index types with indexed access**, the **typeof type operator**, **const assertions**, the **satisfies operator**, the **non-null assertion operator (!)**, and the **definite assignment assertion (!)**. These features enable type-safe property key extraction, compile-time type resolution, type transformation patterns, string pattern types, dynamic property access, type extraction from values, literal type narrowing, type validation without type narrowing, non-null assertions for nullable types, and property initialization control. These features are essential for building type-safe property accessors, generic utility functions, advanced type manipulations, working with nullable types safely, and managing strict property initialization requirements.
66

77
### ✨ New Features
88

@@ -613,6 +613,66 @@ js::string testFunctionNonNull() {
613613
614614
**Note:** The non-null assertion operator is a TypeScript compile-time construct that tells the compiler to treat nullable values as non-null. Since it doesn't affect runtime behavior, TypeScript2Cxx implements pass-through behavior, transpiling the underlying expression directly. In production C++, this provides optimal performance by avoiding runtime null checks, while in debug builds, runtime null checks could be added if needed.
615615
616+
#### Definite Assignment Assertion (!) Support
617+
618+
TypeScript definite assignment assertion is now supported, allowing developers to suppress TypeScript's strict property initialization checking. This feature tells the TypeScript compiler that a property will be assigned before it's used, even if the assignment isn't visible in the declaration.
619+
620+
**TypeScript Code:**
621+
622+
```typescript
623+
class ConfigManager {
624+
// Definite assignment assertion - property will be assigned in initialize()
625+
apiUrl!: string;
626+
timeout!: number;
627+
debug?: boolean; // Optional property for comparison
628+
629+
// Additional properties with assertions
630+
initialized!: boolean;
631+
settings!: { [key: string]: any };
632+
633+
initialize(url: string, timeoutMs: number) {
634+
this.apiUrl = url;
635+
this.timeout = timeoutMs;
636+
this.initialized = true;
637+
this.settings = { theme: "dark" };
638+
}
639+
640+
isReady(): boolean {
641+
return this.initialized;
642+
}
643+
}
644+
645+
// Usage
646+
const config = new ConfigManager();
647+
config.initialize("https://api.example.com", 5000);
648+
console.log("API URL:", config.apiUrl);
649+
```
650+
651+
**Generated C++ Code:**
652+
653+
```cpp
654+
class ConfigManager {
655+
public:
656+
js::string apiUrl; // No initializer - will be set in initialize()
657+
js::number timeout; // No initializer - will be set in initialize()
658+
bool debug; // Optional property
659+
bool initialized; // Will be set in initialize()
660+
js::any settings; // Will be set in initialize()
661+
662+
virtual auto initialize(js::string url, js::number timeoutMs);
663+
virtual bool isReady();
664+
};
665+
666+
auto ConfigManager::initialize(js::string url, js::number timeoutMs) {
667+
this->apiUrl = url;
668+
this->timeout = timeoutMs;
669+
this->initialized = true;
670+
this->settings = js::any(/* object literal */);
671+
}
672+
```
673+
674+
**Note:** The definite assignment assertion (`!`) is a TypeScript compile-time feature that suppresses strict property initialization checking. In C++, this translates to declaring properties without initializers, which is the natural behavior. TypeScript2Cxx handles this automatically - properties with definite assignment assertions are transpiled as regular C++ member variables that are assigned later in constructors or initialization methods.
675+
616676
### 🔧 Implementation Details
617677
618678
- **Type System**:

TODO.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ Based on analysis of both reference implementations:
244244
- ✅ Const assertions (`as const`) (v0.8.6-dev)
245245
- [x] Satisfies operator (v0.8.6-dev)
246246
- [x] Non-null assertion operator (!) (v0.8.6-dev)
247-
- Definite assignment assertion (!)
247+
- [x] Definite assignment assertion (!) (v0.8.6-dev)
248248
- Const type parameters
249249
- NoInfer utility type
250250
- TypedArray support
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
/**
2+
* Definite assignment assertion (!) test cases
3+
* Tests the TypeScript ! operator on property declarations to suppress
4+
* TypeScript's strict property initialization checking
5+
*/
6+
7+
// Basic class with definite assignment assertion
8+
class BasicClass {
9+
// Definite assignment assertion - property will be assigned in constructor
10+
value!: string;
11+
12+
// Another definite assignment
13+
count!: number;
14+
15+
// Optional property for comparison (no assertion needed)
16+
optional?: boolean;
17+
18+
constructor() {
19+
this.value = "initialized";
20+
this.count = 42;
21+
// optional is not assigned - that's fine
22+
}
23+
24+
updateValue(newValue: string) {
25+
this.value = newValue;
26+
}
27+
28+
getValue(): string {
29+
return this.value;
30+
}
31+
}
32+
33+
// Interface for configuration (no definite assignment assertions in interfaces)
34+
interface Config {
35+
apiUrl: string; // Will be set by initialization method
36+
timeout: number; // Will be set by initialization method
37+
debug?: boolean; // Optional, may not be set
38+
}
39+
40+
// Class implementing interface with assertions
41+
class ConfigManager implements Config {
42+
apiUrl!: string;
43+
timeout!: number;
44+
debug?: boolean;
45+
46+
// Additional properties with assertions
47+
initialized!: boolean;
48+
settings!: { [key: string]: any };
49+
50+
initialize(url: string, timeoutMs: number) {
51+
this.apiUrl = url;
52+
this.timeout = timeoutMs;
53+
this.initialized = true;
54+
this.settings = { theme: "dark" };
55+
}
56+
57+
isReady(): boolean {
58+
return this.initialized;
59+
}
60+
}
61+
62+
// Inheritance with definite assignment assertions
63+
abstract class BaseService {
64+
serviceName!: string; // Subclasses must set this
65+
version!: number; // Subclasses must set this
66+
67+
abstract init(): void;
68+
69+
getInfo(): string {
70+
return `${this.serviceName} v${this.version}`;
71+
}
72+
}
73+
74+
class UserService extends BaseService {
75+
userCount!: number; // Will be set in init
76+
77+
init(): void {
78+
this.serviceName = "UserService";
79+
this.version = 1;
80+
this.userCount = 0;
81+
}
82+
83+
addUser(): void {
84+
this.userCount++;
85+
}
86+
}
87+
88+
// Generic class with definite assignment assertions
89+
class Container<T> {
90+
data!: T;
91+
size!: number;
92+
capacity!: number;
93+
94+
initialize(item: T, cap: number): void {
95+
this.data = item;
96+
this.size = 1;
97+
this.capacity = cap;
98+
}
99+
100+
getData(): T {
101+
return this.data;
102+
}
103+
}
104+
105+
// Static properties with definite assignment assertions
106+
class StaticExample {
107+
static globalConfig: { [key: string]: any }; // Will be initialized in setup()
108+
static initialized: boolean; // Will be initialized in setup()
109+
110+
instanceData!: string;
111+
112+
static setup(): void {
113+
StaticExample.globalConfig = { mode: "production" };
114+
StaticExample.initialized = true;
115+
}
116+
117+
constructor(data: string) {
118+
this.instanceData = data;
119+
}
120+
}
121+
122+
// Mixed property types
123+
class MixedProperties {
124+
// Definite assignment assertions
125+
definiteString!: string;
126+
definiteNumber!: number;
127+
definiteBoolean!: boolean;
128+
definiteObject!: { id: number; name: string };
129+
definiteArray!: string[];
130+
131+
// Optional properties
132+
optionalString?: string;
133+
optionalNumber?: number;
134+
135+
// Regular properties with initializers
136+
regularString: string = "default";
137+
regularNumber: number = 0;
138+
139+
setup(): void {
140+
this.definiteString = "assigned";
141+
this.definiteNumber = 123;
142+
this.definiteBoolean = true;
143+
this.definiteObject = { id: 1, name: "test" };
144+
this.definiteArray = ["a", "b", "c"];
145+
}
146+
}
147+
148+
// Readonly with definite assignment assertion
149+
class ReadonlyExample {
150+
readonly id!: number; // Will be set in constructor
151+
readonly name!: string; // Will be set in constructor
152+
readonly created!: Date; // Will be set in constructor
153+
154+
constructor(id: number, name: string) {
155+
// TypeScript allows assignment in constructor even for readonly
156+
(this as any).id = id;
157+
(this as any).name = name;
158+
(this as any).created = new Date();
159+
}
160+
}
161+
162+
// Main function to test all scenarios
163+
function main(): void {
164+
console.log("Definite assignment assertion test started");
165+
166+
// Test BasicClass
167+
const basic = new BasicClass();
168+
console.log("Basic value:", basic.getValue());
169+
basic.updateValue("updated");
170+
console.log("Updated value:", basic.getValue());
171+
172+
// Test ConfigManager
173+
const config = new ConfigManager();
174+
config.initialize("https://api.example.com", 5000);
175+
console.log("Config ready:", config.isReady());
176+
console.log("API URL:", config.apiUrl);
177+
178+
// Test UserService
179+
const userService = new UserService();
180+
userService.init();
181+
console.log("Service info:", userService.getInfo());
182+
userService.addUser();
183+
184+
// Test Container
185+
const stringContainer = new Container<string>();
186+
stringContainer.initialize("hello", 10);
187+
console.log("Container data:", stringContainer.getData());
188+
189+
// Test StaticExample
190+
StaticExample.setup();
191+
const _staticExample = new StaticExample("instance");
192+
console.log("Static initialized:", StaticExample.initialized);
193+
194+
// Test MixedProperties
195+
const mixed = new MixedProperties();
196+
mixed.setup();
197+
console.log("Mixed string:", mixed.definiteString);
198+
console.log("Regular string:", mixed.regularString);
199+
200+
// Test ReadonlyExample
201+
const readonly = new ReadonlyExample(42, "readonly test");
202+
console.log("Readonly ID:", readonly.id);
203+
204+
console.log("Definite assignment assertion test completed");
205+
}
206+
207+
main();

0 commit comments

Comments
 (0)