Skip to content

Commit 10f3d49

Browse files
2 parents c05c068 + 695bd70 commit 10f3d49

12 files changed

+502
-605
lines changed

API-Breaking-Changes.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
# TypeScript 3.7
2+
3+
- the typeArguments property from the TypeReference interface and provides a getTypeArguments method on the TypeChecker interface to be used instead. This change is necessary because resolution of type arguments in type references is now deferred due to the ability to create [Recursive type references](https://github.com/microsoft/TypeScript/pull/33050).
4+
5+
16
# TypeScript 3.1
27

38
- `SymbolFlags.JSContainer` has been renamed to `SymbolFlags.Assignment` to reflect that Typescript now supports expando assignments to functions.

Breaking-Changes.md

Lines changed: 356 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,317 @@ These changes list where implementation differs between versions as the spec and
22

33
> For breaking changes to the compiler/services API, please check the [[API Breaking Changes]] page.
44
5+
# TypeScript 3.6
6+
7+
8+
## Class Members Named `"constructor"` Are Now Constructors
9+
10+
As per the ECMAScript specification, class declarations with methods named `constructor` are now constructor functions, regardless of whether they are declared using identifier names, or string names.
11+
12+
```ts
13+
class C {
14+
"constructor"() {
15+
console.log("I am the constructor now.");
16+
}
17+
}
18+
```
19+
20+
A notable exception, and the workaround to this break, is using a computed property whose name evaluates to `"constructor"`.
21+
22+
```ts
23+
class D {
24+
["constructor"]() {
25+
console.log("I'm not a constructor - just a plain method!");
26+
}
27+
}
28+
```
29+
30+
## DOM Updates
31+
32+
Many declarations have been removed or changed within `lib.dom.d.ts`.
33+
This includes (but isn't limited to) the following:
34+
35+
* The global `window` is no longer defined as type `Window` - instead, it is defined as type `Window & typeof globalThis`. In some cases, it may be better to refer to its type as `typeof window`.
36+
* `GlobalFetch` is gone. Instead, use `WindowOrWorkerGlobalScope`
37+
* Certain non-standard properties on `Navigator` are gone.
38+
* The `experimental-webgl` context is gone. Instead, use `webgl` or `webgl2`.
39+
40+
## JSDoc Comments No Longer Merge
41+
42+
In JavaScript files, TypeScript will only consult immediately preceding JSDoc comments to figure out declared types.
43+
44+
```ts
45+
/**
46+
* @param {string} arg
47+
*/
48+
/**
49+
* oh, hi, were you trying to type something?
50+
*/
51+
function whoWritesFunctionsLikeThis(arg) {
52+
// 'arg' has type 'any'
53+
}
54+
```
55+
56+
## Keywords Cannot Contain Escape Sequences
57+
58+
Previously keywords were not allowed to contain escape sequences.
59+
TypeScript 3.6 disallows them.
60+
61+
```ts
62+
while (true) {
63+
\u0063ontinue;
64+
// ~~~~~~~~~~~~~
65+
// error! Keywords cannot contain escape characters.
66+
}
67+
```
68+
569
# TypeScript 3.5
670

71+
## Generic type parameters are implicitly constrained to `unknown`
72+
73+
In TypeScript 3.5, [generic type parameters without an explicit constraint are now implicitly constrained to `unknown`](https://github.com/Microsoft/TypeScript/pull/30637), whereas previously the implicit constraint of type parameters was the empty object type `{}`.
74+
75+
In practice, `{}` and `unknown` are pretty similar, but there are a few key differences:
76+
77+
* `{}` can be indexed with a string (`k["foo"]`), though this is an implicit `any` error under `--noImplicitAny`.
78+
* `{}` is assumed to not be `null` or `undefined`, whereas `unknown` is possibly one of those values.
79+
* `{}` is assignable to `object`, but `unknown` is not.
80+
81+
On the caller side, this typically means that assignment to `object` will fail, and methods on `Object` like `toString`, `toLocaleString`, `valueOf`, `hasOwnProperty`, `isPrototypeOf`, and `propertyIsEnumerable` will no longer be available.
82+
83+
```ts
84+
function foo<T>(x: T): [T, string] {
85+
return [x, x.toString()]
86+
// ~~~~~~~~ error! Property 'toString' does not exist on type 'T'.
87+
}
88+
```
89+
90+
As a workaround, you can add an explicit constraint of `{}` to a type parameter to get the old behavior.
91+
92+
```ts
93+
// vvvvvvvvvv
94+
function foo<T extends {}>(x: T): [T, string] {
95+
return [x, x.toString()]
96+
}
97+
```
98+
99+
From the caller side, failed inferences for generic type arguments will result in `unknown` instead of `{}`.
100+
101+
```ts
102+
function parse<T>(x: string): T {
103+
return JSON.parse(x);
104+
}
105+
106+
// k has type 'unknown' - previously, it was '{}'.
107+
const k = parse("...");
108+
```
109+
110+
As a workaround, you can provide an explicit type argument:
111+
112+
```ts
113+
// 'k' now has type '{}'
114+
const k = parse<{}>("...");
115+
```
116+
117+
### `{ [k: string]: unknown }` is no longer a wildcard assignment target
118+
119+
The index signature `{ [s: string]: any }` in TypeScript behaves specially: it's a valid assignment target for any object type.
120+
This is a special rule, since types with index signatures don't normally produce this behavior.
121+
122+
Since its introduction, the type `unknown` in an index signature behaved the same way:
123+
124+
```ts
125+
let dict: { [s: string]: unknown };
126+
// Was OK
127+
dict = () => {};
128+
```
129+
130+
In general this rule makes sense; the implied constraint of "all its properties are some subtype of `unknown`" is trivially true of any object type.
131+
However, in TypeScript 3.5, this special rule is removed for `{ [s: string]: unknown }`.
132+
133+
This was a necessary change because of the change from `{}` to `unknown` when generic inference has no candidates.
134+
Consider this code:
135+
136+
```ts
137+
declare function someFunc(): void;
138+
declare function fn<T>(arg: { [k: string]: T }): void;
139+
fn(someFunc);
140+
```
141+
142+
In TypeScript 3.4, the following sequence occurred:
143+
144+
* No candidates were found for `T`
145+
* `T` is selected to be `{}`
146+
* `someFunc` isn't assignable to `arg` because there are no special rules allowing arbitrary assignment to `{ [k: string]: {} }`
147+
* The call is correctly rejected
148+
149+
Due to changes around unconstrained type parameters falling back to `unknown` (see above), `arg` would have had the type `{ [k: string]: unknown }`, which anything is assignable to, so the call would have incorrectly been allowed.
150+
That's why TypeScript 3.5 removes the specialized assignability rule to permit assignment to `{ [k: string]: unknown }`.
151+
152+
Note that fresh object literals are still exempt from this check.
153+
154+
```ts
155+
const obj = { m: 10 };
156+
// OK
157+
const dict: { [s: string]: unknown } = obj;
158+
```
159+
160+
Depending on the intended behavior of `{ [s: string]: unknown }`, several alternatives are available:
161+
162+
* `{ [s: string]: any }`
163+
* `{ [s: string]: {} }`
164+
* `object`
165+
* `unknown`
166+
* `any`
167+
168+
We recommend sketching out your desired use cases and seeing which one is the best option for your particular use case.
169+
170+
## Improved excess property checks in union types
171+
172+
### Background
173+
174+
TypeScript has a feature called *excess property checking* in object literals.
175+
This feature is meant to detect typos for when a type isn't expecting a specific property.
176+
177+
```ts
178+
type Style = {
179+
alignment: string,
180+
color?: string
181+
};
182+
183+
const s: Style = {
184+
alignment: "center",
185+
colour: "grey"
186+
// ^^^^^^ error!
187+
};
188+
```
189+
190+
### Rationale and Change
191+
192+
In TypeScript 3.4 and earlier, certain excess properties were allowed in situations where they really shouldn't have been.
193+
194+
Consider this code:
195+
```ts
196+
type Point = {
197+
x: number;
198+
y: number;
199+
};
200+
201+
type Label = {
202+
name: string;
203+
};
204+
205+
const pl: Point | Label = {
206+
x: 0,
207+
y: 0,
208+
name: true // <- danger!
209+
};
210+
```
211+
212+
Excess property checking was previously only capable of detecting properties which weren't present in *any* member of a target union type.
213+
214+
In TypeScript 3.5, these excess properties are now correctly detected, and the sample above correctly issues an error.
215+
216+
Note that it's still legal to be assignable to multiple parts of a union:
217+
218+
```ts
219+
const pl: Point | Label = {
220+
x: 0,
221+
y: 0,
222+
name: "origin" // OK
223+
};
224+
```
225+
226+
### Workarounds
227+
228+
We have not witnessed examples where this checking hasn't caught legitimate issues, but in a pinch, any of the workarounds to disable excess property checking will apply:
229+
230+
* Add a type assertion onto the object (e.g. `{ myProp: SomeType } as ExpectedType`)
231+
* Add an index signature to the expected type to signal that unspecified properties are expected (e.g. `interface ExpectedType { myProp: SomeType; [prop: string]: unknown }`)
232+
233+
## Fixes to Unsound Writes to Indexed Access Types
234+
235+
### Background
236+
237+
TypeScript allows you to represent the abstract operation of accessing a property of an object via the name of that property:
238+
239+
```ts
240+
type A = {
241+
s: string;
242+
n: number;
243+
};
244+
245+
function read<K extends keyof A>(arg: A, key: K): A[K] {
246+
return arg[key];
247+
}
248+
249+
const a: A = { s: "", n: 0 };
250+
const x = read(a, "s"); // x: string
251+
```
252+
253+
While commonly used for reading values from an object, you can also use this for writes:
254+
255+
```ts
256+
function write<K extends keyof A>(arg: A, key: K, value: A[K]): void {
257+
arg[key] = value;
258+
}
259+
```
260+
261+
### Change and Rationale
262+
263+
In TypeScript 3.4, the logic used to validate a *write* was much too permissive:
264+
265+
```ts
266+
function write<K extends keyof A>(arg: A, key: K, value: A[K]): void {
267+
// ???
268+
arg[key] = "hello, world";
269+
}
270+
// Breaks the object by putting a string where a number should be
271+
write(a, "n");
272+
```
273+
274+
In TypeScript 3.5, this logic is fixed and the above sample correctly issues an error.
275+
276+
### Workarounds
277+
278+
Most instances of this error represent potential errors in the relevant code.
279+
280+
One example we found looked like this:
281+
```ts
282+
type T = {
283+
a: string,
284+
x: number,
285+
y: number
286+
};
287+
function write<K extends keyof T>(obj: T, k: K) {
288+
// Trouble waiting
289+
obj[k] = 1;
290+
}
291+
const someObj: T = { a: "", x: 0, y: 0 };
292+
// Note: write(someObj, "a") never occurs, so the code is technically bug-free (?)
293+
write(someObj, "x");
294+
write(someObj, "y");
295+
```
296+
297+
This function can be fixed to only accept keys which map to numeric properties:
298+
299+
```ts
300+
// Generic helper type that produces the keys of an object
301+
// type which map to properties of some other specific type
302+
type KeysOfType<TObj, TProp, K extends keyof TObj = keyof TObj> = K extends K ? TObj[K] extends TProp ? K : never : never;
303+
304+
function write(obj: SomeObj, k: KeysOfType<SomeObj, number>) {
305+
// OK
306+
obj[k] = 1;
307+
}
308+
309+
const someObj: SomeObj = { a: "", x: 0, y: 0 };
310+
write(someObj, "x");
311+
write(someObj, "y");
312+
// Correctly an error
313+
write(someObj, "a");
314+
```
315+
7316
## `lib.d.ts` includes the `Omit` helper type
8317

9318
TypeScript 3.5 includes a new `Omit` helper type.
@@ -18,6 +327,52 @@ Two workarounds may be used here:
18327
1. Delete the duplicate declaration and use the one provided in `lib.d.ts`.
19328
2. Export the existing declaration from a module file or a namespace to avoid a global collision. Existing usages can use an `import` or explicit reference to your project's old `Omit` type.
20329

330+
## `Object.keys` rejects primitives in ES5
331+
332+
### Background
333+
334+
In ECMAScript 5 environments, `Object.keys` throws an exception if passed any non-`object` argument:
335+
336+
```ts
337+
// Throws if run in an ES5 runtime
338+
Object.keys(10);
339+
```
340+
341+
In ECMAScript 2015, `Object.keys` returns `[]` if its argument is a primitive:
342+
343+
```ts
344+
// [] in ES6 runtime
345+
Object.keys(10);
346+
```
347+
348+
### Rationale and Change
349+
350+
This is a potential source of error that wasn't previously identified.
351+
352+
In TypeScript 3.5, if `target` (or equivalently `lib`) is `ES5`, calls to `Object.keys` must pass a valid `object`.
353+
354+
### Workarounds
355+
356+
In general, errors here represent possible exceptions in your application and should be treated as such.
357+
If you happen to know through other means that a value is an `object`, a type assertion is appropriate:
358+
359+
```ts
360+
function fn(arg: object | number, isArgActuallyObject: boolean) {
361+
if (isArgActuallyObject) {
362+
const k = Object.keys(arg as object);
363+
}
364+
}
365+
```
366+
367+
Note that this change interacts with the change in generic inference from `{}` to `unknown`, because `{}` is a valid `object` whereas `unknown` isn't:
368+
369+
```ts
370+
declare function fn<T>(): T;
371+
372+
// Was OK in TypeScript 3.4, errors in 3.5 under --target ES5
373+
Object.keys(fn());
374+
```
375+
21376
# TypeScript 3.4
22377

23378
## Top-level `this` is now typed
@@ -1709,7 +2064,7 @@ class C {
17092064
foo(arguments: any) { // Invalid: "arguments" is not allow as a function argument
17102065
var eval = 10; // Invalid: "eval" is not allowed as the left-hand-side expression
17112066
arguments = []; // Invalid: arguments object is immutable
1712-
}
2067+
}
17132068
}
17142069
```
17152070
For complete list of strict mode restrictions, please see Annex C - The Strict Mode of ECMAScript of ECMA-262 6<sup>th</sup> Edition.

0 commit comments

Comments
 (0)