Skip to content

Commit 86c42b0

Browse files
committed
feat: add unknown on catch
1 parent ae02fd8 commit 86c42b0

14 files changed

+226
-106
lines changed

drafts/variables.ts

-3
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
try { throw new Error() }
2+
catch (e) {
3+
// handle error
4+
}
5+
6+
try { throw new Error() }
7+
catch (e: any) {
8+
// handle error
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
try { throw new Error() }
2+
catch (e: unknown) {
3+
if (e instanceof Error) {
4+
console.error(e.message)
5+
}
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { checkUnknown } from 'type-plus'
2+
class ErrorA extends Error { }
3+
class ErrorB extends Error { }
4+
5+
try { throw new ErrorA() }
6+
catch (e: unknown) {
7+
if (checkUnknown(e, ErrorA)) {
8+
// handle ErrorA
9+
} else if (checkUnknown(e, ErrorB)) {
10+
// handle ErrorB
11+
} else {
12+
// handle generic Error
13+
}
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
function castNumber(value: unknown) {
2+
return typeof value === 'number' ? value : -1
3+
}
4+
5+
castNumber(1) // 1
6+
castNumber({ a: 1 }) // -1
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"compilerOptions": {
3+
"composite": true,
4+
"esModuleInterop": true,
5+
"moduleResolution": "node",
6+
"outDir": "out",
7+
"target": "ESNext",
8+
"strict": true
9+
}
10+
}

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
"semantic-release": "^17.0.4",
4646
"standard-version": "^7.1.0",
4747
"ts-node": "^8.8.1",
48-
"type-plus": "^1.35.1",
48+
"type-plus": "^2.2.0",
4949
"typescript": "^4.0.2"
5050
},
5151
"remarkConfig": {

pages/00-updates/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ TypeScript 4.0 has landed on Augest 20th, 2020.
88
- [Labeled Tuple](/pages/04-typescript-syntax/tuple-type.md#labeled-tuple-elements)
99
- Class Property Inference from Constructors
1010
- Short-Circuting Assignement Operators
11-
- unknown on catch clauses
11+
- [`unknown` on `catch` clauses](/pages/04-typescript-syntax/unknown.md#unknown-on-catch-clauses)
1212
- Custom JSX Factories
1313

1414
## TypeScript 3.9

pages/04-typescript-syntax/any.md

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# any
2+
3+
You **should** avoid asserting type as `any`.
4+
5+
> Why?
6+
7+
`as any` is a very powerful statement telling the compiler to turn off type checking.
8+
9+
> Why not?
10+
11+
There are times that the compiler cannot infer the type correctly,
12+
or the type is specified by other libraries and is incorrect,
13+
or the type is too complicate to create and does not worth the effort to fix.
14+
15+
For these cases, it is okey to use `as any`.
16+
Make sure you make this decision consciously.
17+
18+
---
19+
20+
You **should** avoid annotating method parameters and callbacks as `any`.
21+
22+
> Why?
23+
24+
Annotating function parameters and callbacks as `any` has similar effect as asserting them as `any`.
25+
26+
The compiler might able to infer the type for you through inheritence or function type declaration.
27+
Annotating parameter as `any` overrides that and you loose all benefits of using TypeScript.

pages/04-typescript-syntax/basic-types.md

+3-96
Original file line numberDiff line numberDiff line change
@@ -1,101 +1,8 @@
11
# Basic Types
22

3-
## any
4-
5-
You **should** avoid asserting type as `any`.
6-
7-
> Why?
8-
9-
`as any` is a very powerful statement telling the compiler to turn off type checking.
10-
11-
> Why not?
12-
13-
There are times that the compiler cannot infer the type correctly,
14-
or the type is specified by other libraries and is incorrect,
15-
or the type is too complicate to create and does not worth the effort to fix.
16-
17-
For these cases, it is okey to use `as any`.
18-
Make sure you make this decision consciously.
19-
20-
---
21-
22-
You **should** avoid annotating method parameters and callbacks as `any`.
23-
24-
> Why?
25-
26-
Annotating function parameters and callbacks as `any` has similar effect as asserting them as `any`.
27-
28-
The compiler might able to infer the type for you through inheritence or function type declaration.
29-
Annotating parameter as `any` overrides that and you loose all benefits of using TypeScript.
30-
31-
---
32-
33-
You **may** use `as unknown`.
34-
35-
### References
36-
37-
- <https://github.com/Microsoft/TypeScript/pull/24439>
38-
- <https://mariusschulz.com/blog/the-unknown-type-in-typescript>
39-
40-
## enum
41-
42-
> Enums allow us to define a set of named constants.
43-
44-
- <https://www.typescriptlang.org/docs/handbook/enums.html>
45-
- <https://www.typescriptlang.org/docs/handbook/basic-types.html#enum>
46-
47-
There are a few special rules about `enum` type:
48-
49-
- values can be either `string` or `number`
50-
51-
```ts
52-
enum map { a = 1, b = 'b' }
53-
}
54-
```
55-
56-
- it is nominal typed
57-
58-
```ts
59-
enum JapanVocaloid { miku, luka }
60-
61-
enum ChinaVocaloid { miku, LuoTianyi, XingChen }
62-
63-
let myFavoriteVocaloid = ChinaVocaloid.miku
64-
65-
// error
66-
myFavoriteVocaloid = JapanVocaloid.miku
67-
```
68-
69-
---
70-
71-
You **should avoid** using `enum`. You **should** use `const` instead.
72-
73-
```ts
74-
// bad
75-
enum Color { Red, Green, Blue }
76-
77-
// good
78-
const COLOR = {
79-
Red: 0,
80-
Green: 1,
81-
Blue: 2
82-
} as const
83-
84-
// good
85-
const COLOR_RED = 0
86-
const COLOR_GREEN = 1
87-
const COLOR_BLUE = 2
88-
```
89-
90-
> Why?
91-
92-
While `enum` is more compact,
93-
and provide additional nominal protection,
94-
it is a TypeScript syntax.
95-
It goes against our design principles.
96-
97-
The transpiled code can be different depends on the config options,
98-
and it can get confusing as it may get erased (`const enum`).
3+
- [`any`](./any.md)
4+
- [`unknown`](./unknown.md)
5+
- [`enum`](./enum.md)
996

1007
## References
1018

pages/04-typescript-syntax/enum.md

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# enum
2+
3+
> Enums allow us to define a set of named constants.
4+
5+
- <https://www.typescriptlang.org/docs/handbook/enums.html>
6+
- <https://www.typescriptlang.org/docs/handbook/basic-types.html#enum>
7+
8+
There are a few special rules about `enum` type:
9+
10+
- values can be either `string` or `number`
11+
12+
```ts
13+
enum map { a = 1, b = 'b' }
14+
```
15+
16+
- it is nominal typed
17+
18+
```ts
19+
enum JapanVocaloid { miku, luka }
20+
21+
enum ChinaVocaloid { miku, LuoTianyi, XingChen }
22+
23+
let myFavoriteVocaloid = ChinaVocaloid.miku
24+
25+
// error
26+
myFavoriteVocaloid = JapanVocaloid.miku
27+
```
28+
29+
---
30+
31+
You **should avoid** using `enum`. You **should** use `const` instead.
32+
33+
```ts
34+
// bad
35+
enum Color { Red, Green, Blue }
36+
37+
// good
38+
const COLOR = {
39+
Red: 0,
40+
Green: 1,
41+
Blue: 2
42+
} as const
43+
44+
// good
45+
const COLOR_RED = 0
46+
const COLOR_GREEN = 1
47+
const COLOR_BLUE = 2
48+
```
49+
50+
> Why?
51+
52+
While `enum` is more compact,
53+
and provide additional nominal protection,
54+
it is a TypeScript syntax.
55+
It goes against our design principles.
56+
57+
The transpiled code can be different depends on the config options,
58+
and it can get confusing as it may get erased (`const enum`).

pages/04-typescript-syntax/unknown.md

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# unknown
2+
3+
Introduced in: TypeScript 3.0
4+
5+
The `unknown` type is a top type in the TypeScript type system.
6+
7+
In essence, use it to describe values that you do not know the type (hence `unknown`).
8+
It ensures you performing type checks before using the values.
9+
10+
## `unknown` on `catch` clauses
11+
12+
Introduced in TypeScript 4.0
13+
14+
Before TypeScript 4.0, the variable in the `catch` clause is always `any` and cannot be typed explicitly.
15+
In TypeScript 4.0 it can now be typed as either `any` or `unknown`.
16+
17+
You **must** type the variable in the `catch` clause as `unknown`.
18+
19+
❌bad
20+
21+
```ts file=../../examples/unknown/standard/catch-clauses.bad.ts
22+
try { throw new Error() }
23+
catch (e) {
24+
// handle error
25+
}
26+
27+
try { throw new Error() }
28+
catch (e: any) {
29+
// handle error
30+
}
31+
```
32+
33+
✔️ good
34+
35+
```ts file=../../examples/unknown/standard/catch-clauses.good.ts
36+
try { throw new Error() }
37+
catch (e: unknown) {
38+
if (e instanceof Error) {
39+
console.error(e.message)
40+
}
41+
}
42+
```
43+
44+
✔️ good
45+
46+
```ts file=../../examples/unknown/standard/catch-clauses.type-plus.good.ts
47+
import { checkUnknown } from 'type-plus'
48+
class ErrorA extends Error { }
49+
class ErrorB extends Error { }
50+
51+
try { throw new ErrorA() }
52+
catch (e: unknown) {
53+
if (checkUnknown(e, ErrorA)) {
54+
// handle ErrorA
55+
} else if (checkUnknown(e, ErrorB)) {
56+
// handle ErrorB
57+
} else {
58+
// handle generic Error
59+
}
60+
}
61+
```
62+
63+
> Why?
64+
65+
The `unknown` type is a better type to use in the `catch` clause than `any`.
66+
67+
TypeScript only allows you to type the variable as `any` or `unknown` in the `catch` clause.
68+
This is because the `try` clause can make function calls
69+
and there is no way to control or know what kind of error (or non-error) those functions could throw.
70+
71+
That's why when handling error you should not assume the type would be `Error` or an explicit list that you expect.
72+
And `unknown` is a better type than `any` to help enforcing this fact.
73+
74+
### Additional Readings
75+
76+
- <https://devblogs.microsoft.com/typescript/announcing-typescript-4-0/#unknown-on-catch>
77+
78+
## Additional Readings
79+
80+
- <https://github.com/Microsoft/TypeScript/pull/24439>
81+
- <https://mariusschulz.com/blog/the-unknown-type-in-typescript>
82+
- <https://blog.logrocket.com/when-to-use-never-and-unknown-in-typescript-5e4d6c5799ad/>

tsconfig.json

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"compilerOptions": {
3-
"esModuleInterop": true
3+
"esModuleInterop": true,
4+
"moduleResolution": "node"
45
},
56
"files": [],
67
"references": [
@@ -10,6 +11,9 @@
1011
{
1112
"path": "examples/property-accessor/standard"
1213
},
14+
{
15+
"path": "examples/unknown/standard"
16+
},
1317
{
1418
"path": "examples/tuple/standard"
1519
}

yarn.lock

+4-4
Original file line numberDiff line numberDiff line change
@@ -5685,10 +5685,10 @@ type-fest@^0.8.1:
56855685
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d"
56865686
integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==
56875687

5688-
type-plus@^1.35.1:
5689-
version "1.35.1"
5690-
resolved "https://registry.yarnpkg.com/type-plus/-/type-plus-1.35.1.tgz#15e39a0922fdf08d8d24cc7d1a5d50d3273434de"
5691-
integrity sha512-El1yp4aLFVpOwU/emJRxqw7+OsA8gUWILnrS8Ln5rafvHSG5tHopvmQLIBPdkFfRk65ZBcipVlk3XcfkL36jng==
5688+
type-plus@^2.2.0:
5689+
version "2.2.0"
5690+
resolved "https://registry.yarnpkg.com/type-plus/-/type-plus-2.2.0.tgz#7d76d01e73ac995e6776b9a94b7e0bc3773e20d5"
5691+
integrity sha512-dXjp7T85ZjJRCrZxGzBhAGe9anclXyIMGNBqav5pBNVZcPjBhNP/jWxy4t5oStIFGLSmnTJOv6JqiyVQaStkKA==
56925692
dependencies:
56935693
unpartial "^0.6.3"
56945694

0 commit comments

Comments
 (0)