You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat: support type-only/uninstantiated namespaces (#32)
This PR adds support for type-only/uninstantiated namespaces. i.e.
namespaces that only use `type` and `interface`.
```ts
export class C {}
export namespace C {
export type T = string;
}
```
**Before:** Error. only `declare namespace ...` was allowed ❌
**Now:** Ok. namespace declaration erased ✅
---
The following cases remain unchanged:
```ts
// Instantiated namespace errors
namespace A { export let x = 1 } // error
namespace B { ; } // error
// Ambient namespace is erased
declare namespace C { export let x = 1 } // ok erased
declare module D { export let x = 1 } // ok erased
// `module`-keyword errors
module E { export let x = 1 } // error
module F { export type x = number } // error
```
## Testing
Unit tests for both supported cases and unsupported cases (errors) have
been added.
## Context
This addition was motivated by
[`--erasableSyntaxOnly`](microsoft/TypeScript#61011)
and the recognition that in today's TypeScript, the only way to augment
a class with types after the initial declaration is via namespaces.
Whilst that use case can be solved using `declare namespace` that comes
with [a
hazard](https://github.com/bloomberg/ts-blank-space/blob/main/docs/unsupported_syntax.md#the-declare--hazard).
The combination of `--erasableSyntaxOnly` checks and transforming
uninstantiated namespaces provides a safer option.
Thanks to @jakebailey for brining this to our attention
microsoft/TypeScript#61011 (comment)
---------
Signed-off-by: Ashley Claymore <[email protected]>
Co-authored-by: Rob Palmer <[email protected]>
For more details on use of `declare` see [the `declare` hazard](#the-declare--hazard).
17
+
18
+
### Enums
19
+
20
+
The following `enum` declaration will not be transformed by `ts-blank-space`.
17
21
18
22
```typescript
19
23
enumDirection {
@@ -24,7 +28,7 @@ enum Direction {
24
28
}
25
29
```
26
30
27
-
Alternative approach to defining an enum like value and type, which is `ts-blank-space` compatible:
31
+
An alternative approach to defining an enum like value and type, which is `ts-blank-space` compatible:
28
32
29
33
```typescript
30
34
const Direction = {
@@ -39,13 +43,16 @@ type Direction = (typeof Direction)[keyof typeof Direction];
39
43
40
44
### Constructor Parameter Properties
41
45
46
+
The following usage of a constructor parameter property will not be transformed by `ts-blank-space`.
47
+
42
48
```typescript
43
49
classPerson {
44
50
constructor(publicname:string) {}
51
+
// ^^^^^^
45
52
}
46
53
```
47
54
48
-
Alternative`ts-blank-space` compatible approach:
55
+
The equivalent`ts-blank-space` compatible approach:
49
56
50
57
```typescript
51
58
classPerson {
@@ -56,6 +63,62 @@ class Person {
56
63
}
57
64
```
58
65
66
+
### `namespace` declarations
67
+
68
+
While sharing the same syntax there are technically two categories of `namespace` within TypeScript. Instantiated and non-instantiated. Instantiated namespaces create objects that exist at runtime. Non-instantiated namespaces can be erased. A namespace is non-instantiated if it only contains types - more specifically it may only contain:
69
+
70
+
- type aliases: `[export] type A = ...`
71
+
- interfaces: `[export] interface I { ... }`
72
+
- Importing types from other namespaces: `import A = OtherNamespace.X`
73
+
- More non-instantiated namespaces (the rule is recursive)
74
+
75
+
`ts-blank-space` will always erase non-instantiated namespaces and namespaces marked with [`declare`](#the-declare--hazard).
76
+
77
+
Examples of supported namespace syntax can be seen in the test fixture [tests/fixture/cases/namespaces.ts](../tests/fixture/cases/namespaces.ts). Error cases can be seen in [tests/errors](../tests/errors.test.ts).
78
+
79
+
### `module` namespace declarations
80
+
81
+
`ts-blank-space` only erases TypeScript's `module` namespace declarations if they are marked with `declare` (see [`declare` hazard](#the-declare--hazard)).
82
+
83
+
All other TypeScript `module` declarations will trigger the `onError` callback and be left in the output text verbatim. Including an empty declaration:
84
+
85
+
```ts
86
+
moduleM {} // `ts-blank-space` error
87
+
```
88
+
89
+
Note that, since TypeScript 5.6, use of `module` namespace declarations (not to be confused with _"ambient module declarations"_) will be shown with a strike-through (~~`module`~~) to hint that the syntax is deprecated in favour of [`namespace`](#namespace-declarations).
90
+
91
+
See https://github.com/microsoft/TypeScript/issues/51825 for more information.
92
+
93
+
### The `declare ...` hazard
94
+
95
+
As with `declare const ...`, while `ts-blank-space` will erase syntax such as `declare enum ...` and `declare namespace ...` without error it should be used with understanding and mild caution.
96
+
`declare` in TypeScript is an _assertion_ by the author that a value will exist at runtime.
97
+
98
+
For example:
99
+
100
+
<!-- prettier-ignore -->
101
+
```ts
102
+
declarenamespaceN {
103
+
exportconst x:number;
104
+
}
105
+
console.log(N.x);
106
+
```
107
+
108
+
The above will not be a build time error and will be transformed to:
109
+
110
+
<!-- prettier-ignore -->
111
+
```js
112
+
113
+
114
+
115
+
console.log(N.x);
116
+
```
117
+
118
+
So it may throw at runtime if nothing created a runtime value for `N` as promised by the `declare`.
119
+
120
+
Tests are a great way to catch issues that may arise from an incorrect `declare`.
121
+
59
122
## Compile time only syntax
60
123
61
124
TypeScript type assertions have no runtime semantics, however `ts-blank-space` does not erase the legacy prefix-style type assertions.
0 commit comments