-
Notifications
You must be signed in to change notification settings - Fork 234
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Update code samples and home page for second edition (#34)
* Update code samples and home page for second edition * second edition cover * back to old image
- Loading branch information
Showing
667 changed files
with
12,224 additions
and
6,645 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
# Item 45: Hide Unsafe Type Assertions in Well-Typed Functions | ||
|
||
## Things to Remember | ||
|
||
- Sometimes unsafe type assertions and `any` types are necessary or expedient. When you need to use one, hide it inside a function with a correct signature. | ||
- Don't compromise a function's type signature to fix type errors in the implementation. | ||
- Make sure you explain why your type assertions are valid, and unit test your code thoroughly. | ||
|
||
## Code Samples | ||
|
||
```ts | ||
interface MountainPeak { | ||
name: string; | ||
continent: string; | ||
elevationMeters: number; | ||
firstAscentYear: number; | ||
} | ||
|
||
async function checkedFetchJSON(url: string): Promise<unknown> { | ||
const response = await fetch(url); | ||
if (!response.ok) { | ||
throw new Error(`Unable to fetch! ${response.statusText}`); | ||
} | ||
return response.json(); | ||
} | ||
|
||
export async function fetchPeak(peakId: string): Promise<MountainPeak> { | ||
return checkedFetchJSON(`/api/mountain-peaks/${peakId}`); | ||
// ~~~~~ Type 'unknown' is not assignable to type 'MountainPeak'. | ||
} | ||
``` | ||
|
||
[💻 playground](https://www.typescriptlang.org/play/?ts=5.4.5#code/JYOwLgpgTgZghgYwgAgLIHsCu46gAoRwDWyA3gFDLIhwC2EAXMgM5hSgDmA3JcguuFARwTVuxDdeEADYQAbnDDABqCJCjMmITLQBG0HlRjANYAILMk4AJqEoWnfqg8AvuXJxmATxAJkMbAQlAT4ACwgEIggAEwAxNQRQgCkAZQB5ADkACkwoaVE2TgBKJjwodFpgZggAHmwiEHQAdxAAPjJefhBWZCgIZgAHAWrkAF5kOCbcMH8E0Jy8osNkYBhkLIBCPsHhiAA6dCIijqoqMFDypuoIK4BRKHKoLIADAFUaXVlkMHRZsESNsgACSkbZDbr7ViKTDMAAqEAAHmAXM8lrw3FQ+mBciBev1wdU9gArZgCLJotzkRFDKAzTw+PwBXzBXEwOYEYhZAaEIgASWiBXEHBKyDKFSqtQw2DAuBAHKI7QomLUOLCESicTmqUyLwA9HABsBdbQsDhQABabnEZi6kFWvnRFFo3W65AAPw9HuQsK83OQAHJ6o0Wv6VsxqOg6cxmMAOB8vj9vr6UP6pWa5Tz-XtyG4gA) | ||
|
||
---- | ||
|
||
```ts | ||
export async function fetchPeak(peakId: string): Promise<unknown> { | ||
return checkedFetchJSON(`/api/mountain-peaks/${peakId}`); // ok | ||
} | ||
``` | ||
|
||
[💻 playground](https://www.typescriptlang.org/play/?ts=5.4.5#code/JYOwLgpgTgZghgYwgAgLIHsCu46gAoRwDWyA3gFDLIhwC2EAXMgM5hSgDmA3JcguuFARwTVuxDdeEADYQAbnDDABqCJCjMmITLQBG0HlRjANYAILMk4AJqEoWnfqg8AvuXJxmATxAJkMbAQlAT4ACwgEIggAEwAxNQRQgCkAZQB5ADkACkwoaVE2TgBKJjwodFpgZggAHmwiEHQAdxAAPjJefhBWZCgIZgAHAWrkAF5kOCbcMH8E0Jy8osNkYBhkLIBCPsHhiAA6dCIijqoqMFDypuoIK4BRKHKoLIADAFUaXVlkMHRZsESNsgACSkbZDbr7ViKTDMAAqEAAHmAXM8lrw3FQ+mBciBev1wdU9gArZgCLJotzkRFDKAzTw+PwBXzBXEwOYEYhZAaEIgASWiBXEHBKyDKFSqtXqjRa7QomLUOLCESicTmqUyLwA9HABsBNbQsDhQABabnEZiakFmvnRFFLKiazXIQ7kNxAA) | ||
|
||
---- | ||
|
||
```ts | ||
const sevenPeaks = [ | ||
'aconcagua', 'denali', 'elbrus', 'everest', 'kilimanjaro', 'vinson', 'wilhelm' | ||
]; | ||
async function getPeaksByHeight(): Promise<MountainPeak[]> { | ||
const peaks = await Promise.all(sevenPeaks.map(fetchPeak)); | ||
return peaks.toSorted( | ||
// ~~~ Type 'unknown' is not assignable to type 'MountainPeak'. | ||
(a, b) => b.elevationMeters - a.elevationMeters | ||
// ~ ~ 'b' and 'a' are of type 'unknown' | ||
); | ||
} | ||
``` | ||
|
||
[💻 playground](https://www.typescriptlang.org/play/?ts=5.4.5#code/JYOwLgpgTgZghgYwgAgLIHsCu46gAoRwDWyA3gFDLIhwC2EAXMgM5hSgDmA3JcguuFARwTVuxDdeEADYQAbnDDABqCJCjMmITLQBG0HlRjANYAILMk4AJqEoWnfqg8AvuXJxmATxAJkMbAQlAT4ACwgEIggAEwAxNQRQgCkAZQB5ADkACkwoaVE2TgBKJjwodFpgZggAHmwiEHQAdxAAPjJefhBWZCgIZgAHAWrkAF5kOCbcMH8E0Jy8osNkYBhkLIBCPsHhiAA6dCIijqoqMFDypuoIK4BRKHKoLIADAFUaXVlkMHRZsESNsgACSkbZDbr7ViKTDMAAqEAAHmAXM8lrw3FQ+mBciBev1wdU9gArZgCLJotzkRFDKAzTw+PwBXzBXEwOYEYhZAaEIgASWiBXEHBKyDKFSqtXqjRa7QomLUOLCESicTmqUyLwA9HABsBNbQsDhQABabnEZiakFmvnRFFLKiazXIQ7kNxdHrVOTCDlEZhjZAAbV4AHJEAIEHAOJg4MGADTIYPRYRwaTAOMJmS6KAw9PB+TQfpgXNEYCp2hwEBEuDlXNyUCkkC5pql8LSWjB8gAXR49N8-kCLOQHDUPuYACEvAAJCDADihMDk0rlSrVGoYbBgXAgH0BzuyzrDGbWv3jSbTUXLiV7FPSLKe7085h7csDLJs-6hH1FNHy7FQXHHnsPwpOgtIxFkvCOsgAB+sHILCXjcgmUrNI2Kx+o0dLMMws4fF8PzfIhKDBuuRrbjywZ7LwVBZHA8a6McoztLoewyPIijKCAqjqH6xoTKxsgKCy3HQMw1HIFBpxSdBUmyXJ0kJrowYTCA0QJjGEx9M6axgERyEgA0qEdlQFLkEAA) | ||
|
||
---- | ||
|
||
```ts | ||
async function getPeaksByDate(): Promise<MountainPeak[]> { | ||
const peaks = await Promise.all(sevenPeaks.map(fetchPeak)) as MountainPeak[]; | ||
return peaks.toSorted((a, b) => b.firstAscentYear - a.firstAscentYear); | ||
} | ||
``` | ||
|
||
[💻 playground](https://www.typescriptlang.org/play/?ts=5.4.5#code/JYOwLgpgTgZghgYwgAgLIHsCu46gAoRwDWyA3gFDLIhwC2EAXMgM5hSgDmA3JcguuFARwTVuxDdeEADYQAbnDDABqCJCjMmITLQBG0HlRjANYAILMk4AJqEoWnfqg8AvuXJxmATxAJkMbAQlAT4ACwgEIggAEwAxNQRQgCkAZQB5ADkACkwoaVE2TgBKJjwodFpgZggAHmwiEHQAdxAAPjJefhBWZCgIZgAHAWrkAF5kOCbcMH8E0Jy8osNkYBhkLIBCPsHhiAA6dCIijqoqMFDypuoIK4BRKHKoLIADAFUaXVlkMHRZsESNsgACSkbZDbr7ViKTDMAAqEAAHmAXM8lrw3FQ+mBciBev1wdU9gArZgCLJotzkRFDKAzTw+PwBXzBXEwOYEYhZAaEIgASWiBXEHBKyDKFSqtXqjRa7QomLUOLCESicTmqUyLwA9HABsBNbQsDhQABabnEZiakFmvnRFFLKiazXIQ7kNxdHrVOTCDlEZhjZAAbV4AHJEAIEHAOJg4MGADTIYPRYRwaTAOMJmS6KAw9PB+TQfpgXNEYCp2hwEBEuDlXNyUCkkC5pql8LSWjB8gAXR49N8-kCLOQHDUPuYACEvAARRQQcmlcqVao1DDYMC4EA+gOd2WdYYza1+8aTaaihcSvYp6RZT3ennMPblgZZNn-UI+orHTxoQ1r-A8rfLFiioHnsPwpOgtIxFkWRwPGujHKM7S6HsximBYVhgLY1bIMaEwoSYrDocImF2BS5BAA) | ||
|
||
---- | ||
|
||
```ts | ||
export async function fetchPeak(peakId: string): Promise<MountainPeak> { | ||
return checkedFetchJSON( | ||
`/api/mountain-peaks/${peakId}`, | ||
) as Promise<MountainPeak>; | ||
} | ||
``` | ||
|
||
[💻 playground](https://www.typescriptlang.org/play/?ts=5.4.5&target=9#code/JYOwLgpgTgZghgYwgAgLIHsCu46gAoRwDWyA3gFDLIhwC2EAXMgM5hSgDmA3JcguuFARwTVuxDdeEADYQAbnDDABqCJCjMmITLQBG0HlRjANYAILMk4AJqEoWnfqg8AvuXJxmATxAJkMbAQlAT4ACwgEIggAEwAxNQRQgCkAZQB5ADkACkwoaVE2TgBKJjwodFpgZggAHmwiEHQAdxAAPjJefhBWZCgIZgAHAWrkAF5kOCbcMH8E0Jy8osNkYBhkLIBCPsHhiAA6dCIijqoqMFDypuoIK4BRKHKoLIADAFUaXVlkMHRZsESNsgACSkbZDbr7ViKTDMAAqEAAHmAXM8lrw3FQ+mBciBev1wdU9gArZgCLJotzkRFDKAzTw+PwBXzBXEwOYEYhZAaEIgASWiBXEHBKyDKFSqtQw2DAuBAHKI7QomLUOLCESicTmqUyWV4VGeAHo4ANgAbaFgcKAALTc4jMA0g2186IogA0vGOnlF5Uq1RqUstcp5rVc5CAA) | ||
|
||
---- | ||
|
||
```ts | ||
async function getPeaksByContinent(): Promise<MountainPeak[]> { | ||
const peaks = await Promise.all(sevenPeaks.map(fetchPeak)); // no assertion! | ||
return peaks.toSorted((a, b) => a.continent.localeCompare(b.continent)); | ||
} | ||
``` | ||
|
||
[💻 playground](https://www.typescriptlang.org/play/?ts=5.4.5&target=9#code/JYOwLgpgTgZghgYwgAgLIHsCu46gAoRwDWyA3gFDLIhwC2EAXMgM5hSgDmA3JcguuFARwTVuxDdeEADYQAbnDDABqCJCjMmITLQBG0HlRjANYAILMk4AJqEoWnfqg8AvuXJxmATxAJkMbAQlAT4ACwgEIggAEwAxNQRQgCkAZQB5ADkACkwoaVE2TgBKJjwodFpgZggAHmwiEHQAdxAAPjJefhBWZCgIZgAHAWrkAF5kOCbcMH8E0Jy8osNkYBhkLIBCPsHhiAA6dCIijqoqMFDypuoIK4BRKHKoLIADAFUaXVlkMHRZsESNsgACSkbZDbr7ViKTDMAAqEAAHmAXM8lrw3FQ+mBciBev1wdU9gArZgCLJotzkRFDKAzTw+PwBXzBXEwOYEYhZAaEIgASWiBXEHBKyDKFSqtQw2DAuBAHKI7QomLUOLCESicTmqUyWV4VGeAHo4ANgAbaFgcKAALTc4jMA0g2186IogA0vGOnlF5Uq1RqUstcp5rVc5C6PWqcmE8uYY2QAG1eAByRACBBwDiYOBJ13IJPRYRwaTAHN5mS6KAw0tJ+TQfpgatEYDF2hwEBEuDlatyUCkkDVprN8LSWhJ8gAXR49N8-kCLOQHDUMYAQl4AMICJQgYRgcmlH0S-0WmX4Hnx8eKzrDGZO2PjSbTb3iwlF6RZSPRnnMPatgZZNn-KE8pFEsVAGga1C-J41S0soIAbLwWKqreew-Ck6C0jEWRZHAua6McoztHAexdFuO57NI6DprIG60AMnYQFkugkZuQjgCBoZAA) | ||
|
||
---- | ||
|
||
```ts | ||
export async function fetchPeak(peakId: string): Promise<MountainPeak> { | ||
const maybePeak = checkedFetchJSON(`/api/mountain-peaks/${peakId}`); | ||
if ( | ||
!maybePeak || | ||
typeof maybePeak !== 'object' || | ||
!('firstAscentYear' in maybePeak) | ||
) { | ||
throw new Error(`Invalid mountain peak: ${JSON.stringify(maybePeak)}`); | ||
} | ||
return checkedFetchJSON( | ||
`/api/mountain-peaks/${peakId}`, | ||
) as Promise<MountainPeak>; | ||
} | ||
``` | ||
|
||
[💻 playground](https://www.typescriptlang.org/play/?ts=5.4.5#code/JYOwLgpgTgZghgYwgAgLIHsCu46gAoRwDWyA3gFDLIhwC2EAXMgM5hSgDmA3JcguuFARwTVuxDdeEADYQAbnDDABqCJCjMmITLQBG0HlRjANYAILMk4AJqEoWnfqg8AvuXJxmATxAJkMbAQlAT4ACwgEIggAEwAxNQRQgCkAZQB5ADkACkwoaVE2TgBKJjwodFpgZggAHmwiEHQAdxAAPjJefhBWZCgIZgAHAWrkAF5kOCbcMH8E0Jy8osNkYBhkLIBCPsHhiAA6dCIijqoqMFDypuoIK4BRKHKoLIADAFUaXVlkMHRZsESNsgACSkbZDbr7ViKTDMAAqEAAHmAXM8lrw3FQ+mBciBev1wdU9gArZgCLJotzkRFDKAzTw+PwBXzBXEwOYEYhZAaEIgASWiBXEHBKyDKFSqtQw2DAuBAHKI7QoVC6PVocC8+nlYzCESicTmqUyLwA9HABsBjbQsDhQABabnEZjGkEOvnRFFoqirda8KgbNUaiBagA+wd93y83PQawDmp5yA2o3GAHJ0LoiREwMnkKHwxsssnjKYLFYwLY4FBs6BkLGgzyirxjkrTudLtc7g90E9nryQAppMBojXrTLq66mCDDRk9mJOKsvFla-Kih7lhi8dioLjErqYvF-sl0tlw89TebLSPZfaeU6XTz+SiADSNibMUXlSrVGpSm1ynmtVxyCAA) | ||
|
||
---- | ||
|
||
```ts | ||
export async function fetchPeak(peakId: string): Promise<MountainPeak>; | ||
export async function fetchPeak(peakId: string): Promise<unknown> { | ||
return checkedFetchJSON(`/api/mountain-peaks/${peakId}`); // OK | ||
} | ||
|
||
const denali = fetchPeak('denali'); | ||
// ^? const denali: Promise<MountainPeak> | ||
``` | ||
|
||
[💻 playground](https://www.typescriptlang.org/play/?ts=5.4.5#code/JYOwLgpgTgZghgYwgAgLIHsCu46gAoRwDWyA3gFDLIhwC2EAXMgM5hSgDmA3JcguuFARwTVuxDdeEADYQAbnDDABqCJCjMmITLQBG0HlRjANYAILMk4AJqEoWnfqg8AvuXJxmATxAJkMbAQlAT4ACwgEIggAEwAxNQRQgCkAZQB5ADkACkwoaVE2TgBKJjwodFpgZggAHmwiEHQAdxAAPjJefhBWZCgIZgAHAWrkAF5kOCbcMH8E0Jy8osNkYBhkLIBCPsHhiAA6dCIijqoqMFDypuoIK4BRKHKoLIADAFUaXVlkMHRZsESNsgACSkbZDbr7ViKTDMAAqEAAHmAXM8lrw3FQ+mBciBev1wdU9gArZgCLJotzkRFDKAzTw+PwBXzBXEwOYEYhZAaEIgASWiBXEHBKyDKFSqtQw2DAuBAHKIrR41PQtIm3l8-kCLL+iXlXJ5-MFxVK5Uq1TqIAazTaJzx2KguMSESicTmqUyLwA9HABsBPbQsDhQABabnEZiekFhvnRFFLKiez3INIAaXIlK6PWiwjg0mAYx1oT1AHJszQ88W0YnTsgAHoAfj4wxmZdzwBN4vNUqDcp5rXIQA) | ||
|
||
---- | ||
|
||
```ts | ||
function shallowObjectEqual(a: object, b: object): boolean { | ||
for (const [k, aVal] of Object.entries(a)) { | ||
if (!(k in b) || aVal !== b[k]) { | ||
// ~~~~ Element implicitly has an 'any' type | ||
// because type '{}' has no index signature | ||
return false; | ||
} | ||
} | ||
return Object.keys(a).length === Object.keys(b).length; | ||
} | ||
``` | ||
|
||
[💻 playground](https://www.typescriptlang.org/play/?ts=5.4.5#code/GYVwdgxgLglg9mABAZwBYEMA2m4HcDyARgFYCm0AogI4hYAU6AXInCeVADSKHOtnQBKZoThxMpdEgDeAKESJgcAE6I6EBMiiIA2gGsu6AGpYAui2CIi-KADpSYKEpilkDAQMSz58mBboBCOl1EGCRCDwAfCMQjLER-AF4E7j0TDy9veQB6LMy8-IKAP2LCxApxAFt7LRgKgAdMGAgYKEwAT0QMZBikAHJJNt7EKDa60jk8nILpme9CcnQQZFJh0ZXeqQBfIa7EMDgQsAATUgAPFBgAczB0KBAlcfyHu6UkYCxlgG4J+U2Jv-kz3uSCs7BsulIbVc6AENnEYEuUFQiCSyVB0HBkNc4Th9kRqG+fyAA) | ||
|
||
---- | ||
|
||
```ts | ||
function shallowObjectEqualBad(a: object, b: any): boolean { | ||
for (const [k, aVal] of Object.entries(a)) { | ||
if (!(k in b) || aVal !== b[k]) { // ok | ||
return false; | ||
} | ||
} | ||
return Object.keys(a).length === Object.keys(b).length; | ||
} | ||
``` | ||
|
||
[💻 playground](https://www.typescriptlang.org/play/?ts=5.4.5#code/GYVwdgxgLglg9mABAZwBYEMA2m4HcDyARgFYCm0AogI4hYAU6AXInCeVADSKHOtnQBKZoThxMpdEgDeAKESJgcAE6I6EBMiiIA2gGsu6AGpYAui2CIi-KADpSYKEpilkDAQMSz58mBboBCOl1EGCRCDwAfCMQjLER-AF4E7j0TDy9veQB6LMy8-IKAP2LCxApxAFt7LRgKgAdMGAgYKEwAT0QMZBikAHJJNt7EKDa60jk8nILpme9CcnQQZFJh0ZXeqQBfIa7EMDgQsAATUgAPFBgAczB0KBAlcfyHu6UkYCxlgG4J+U2Jv-kz3uSCs7BsulIbVc6AENnEYEuUFQiCSyVB0HBkNc4Th9kRqG+f1AkFgCBQGGweHRUGotEwACF0EcGLw2NAuDwem0hNxROJJJ4JooVGoNFo9AZjJgzHALNS7A4nC43OkfiE-IFgqFuJForFMPEkildGlPNlcnBdGrAaQXm8PqRvpkAYgXUDXpY2bYIVC3LiEUiUUb5T7sbD4fjCTIgA) | ||
|
||
---- | ||
|
||
```ts | ||
shallowObjectEqual({x: 1}, null) | ||
// ~~~~ Type 'null' is not assignable to type 'object'. | ||
shallowObjectEqualBad({x: 1}, null); // ok, throws at runtime | ||
``` | ||
|
||
[💻 playground](https://www.typescriptlang.org/play/?ts=5.4.5#code/GYVwdgxgLglg9mABAZwBYEMA2m4HcDyARgFYCm0AogI4hYAU6AXInCeVADSKHOtnQBKZoThxMpdEgDeAKESJgcAE6I6EBMiiIA2gGsu6AGpYAui2CIi-KADpSYKEpilkDAQMSz58mBboBCOl1EGCRCDwAfCMQjLER-AF4E7j0TDy9veQB6LMy8-IKAP2LCxApxAFt7LRgKgAdMGAgYKEwAT0QMZBikAHJJNt7EKDa60jk8nILpme9CcnQQZFJh0ZXeqQBfIa7EMDgQsAATUgAPFBgAczB0KBAlcfyHu6UkYCxlgG4J+U2Jv-kz3uSCs7BsulIbVc6AENnEYEuUFQiCSyVB0HBkNc4Th9kRqG+f1AkFgCBQGGweHRUGotEwACF0EcGLw2NAuDwem0hNxROJJJ4JooVGoNFo9AZjJgzHALNS7A4nC43OkfiE-IFgqFuJForFMPEkildGlPNlcnBdGrAaQXm8PqRvpkAYgXUDXpY2bYIVC3LiEUiUUb5T7sbD4fjCTI0FgcAQvbT6FJTswAIybLhgEDYAQyKazAveEqlAAqa0QvSz2CGMG6+y06GQyCuN0I4mGBxGYwrfHYvRs0YpcepiYZTLoybTGb22cwAk+5pY+mGqCUeG6t0QSnAsCqMiAA) | ||
|
||
---- | ||
|
||
```ts | ||
function shallowObjectEqualGood(a: object, b: object): boolean { | ||
for (const [k, aVal] of Object.entries(a)) { | ||
if (!(k in b) || aVal !== (b as any)[k]) { | ||
// `(b as any)[k]` is OK because we've just checked `k in b` | ||
return false; | ||
} | ||
} | ||
return Object.keys(a).length === Object.keys(b).length; | ||
} | ||
``` | ||
|
||
[💻 playground](https://www.typescriptlang.org/play/?ts=5.4.5#code/GYVwdgxgLglg9mABAZwBYEMA2m4HcDyARgFYCm0AogI4hYAU6AXInCeVADSKHOtnQBKZoThxMpdEgDeAKESJgcAE6I6EBMiiIA2gGsu6AGpYAui2CIi-KADpSYKEpilkDAQMSz58mBboBCOl1EGCRCDwAfCMQjLER-AF4E7j0TDy9veQB6LMy8-IKAP2LCxApxAFt7LRgKgAdMGAgYKEwAT0QMZBikAHJJNt7EKDa60jk8nILpme9CcnQQZFJh0ZXeqQBfIa7EMDgQsAATUgAPFBgAczB0KBAlcfyHu6UkYCxlgG4J+U2Jv-kz3uSCs7BsulIbVc6AENnEYEuUFQiCSyVB0HBkNc4Th9kRqG+f1AkFgCBQGGweHRUGotEwACF0EcGLw2NAuDwem0hNxROJJJ4JooVGoNFo9AZjJgzHALNS7A4nC43OkfiE-IFgqFuJForFMPEkildGlPNlcnBdGrAaQXm8PqRvpkAYgXUDXpY2bYIVC3LiEUiUUb5T7sbD4fjCTJidB4Eg0FgcAQvbSsABxUTMpgsL0c1nWHkiMQSaRC5SqdRgTQ6fQxKUyuVehWOZzQ9yCzK+VSaw46xBROtxRLJOiEGLdAYCVKq-JTAAGo-HXKnJrnIW6+AA0twFksVrhSL0AG4rYhLLQQVDkCFHRBzrVhOfWxDu+2YL5ql1u23Az3WTG+jC-r4kGaJNqGo7hniSJRkAA) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
# Item 43: Use the Narrowest Possible Scope for any Types | ||
|
||
## Things to Remember | ||
|
||
- Make your uses of `any` as narrowly scoped as possible to avoid undesired loss of type safety elsewhere in your code. | ||
- Never return an `any` type from a function. This will silently lead to the loss of type safety for code that calls the function. | ||
- Use `as any` on individual properties of a larger object instead of the whole object. | ||
|
||
## Code Samples | ||
|
||
```ts | ||
declare function getPizza(): Pizza; | ||
function eatSalad(salad: Salad) { /* ... */ } | ||
|
||
function eatDinner() { | ||
const pizza = getPizza(); | ||
eatSalad(pizza); | ||
// ~~~~~ | ||
// Argument of type 'Pizza' is not assignable to parameter of type 'Salad' | ||
pizza.slice(); | ||
} | ||
``` | ||
|
||
[💻 playground](https://www.typescriptlang.org/play/?ts=5.4.5#code/JYOwLgpgTgZghgYwgAgArAF4bsg3ssAewAdjQBzALmQGcwoKBuWgG2CQAoBKagN0OAATZAF8AUKEixEKAMpwWcYfjABPYhGp0GIcs3GCICRVBQwAriARhghEMnIQw6LHG7UX2RmItWbd5Ag4MHlFQQ4aBSVqUKUuPGQAegAqZAA6DORkxNExH0trW3sgsAARUBBobjwxZGQEOzpkMldkAF4HJ083Lm86ktjwluxe2qScusnkAD9Z2bHEnIBBKHJzAFsIcGRCGAJ1FABybsPkYBpkEEIwZDgaGmByEDgAIxYUIma4KDhNqR29moNMhDoNDmNhnA0jQ2JxRuIgA) | ||
|
||
---- | ||
|
||
```ts | ||
function eatDinner1() { | ||
const pizza: any = getPizza(); // Don't do this | ||
eatSalad(pizza); // ok | ||
pizza.slice(); // This call is unchecked! | ||
} | ||
|
||
function eatDinner2() { | ||
const pizza = getPizza(); | ||
eatSalad(pizza as any); // This is preferable | ||
pizza.slice(); // this is safe | ||
} | ||
``` | ||
|
||
[💻 playground](https://www.typescriptlang.org/play/?ts=5.4.5#code/JYOwLgpgTgZghgYwgAgArAF4bsg3ssAewAdjQBzALmQGcwoKBuWgG2CQAoBKagN0OAATZAF8AUKEixEKAMpwWcYfjABPYhGp0GIcs3GCICRVBQwAriARhghEMnIQw6LHG7UX2RmItWbd5Ag4MHlFQQ4aBSVqUKUuPGQAegAqZAA6DORkxNEfS2tbeyCwABFQEGgARm48MWRkBDs6ZDJXajgQVWQAXgcnTzcuZiSckrsAcjBkQUICAAtgGjrA4Njw1uwh+sScwgBrZY24NJo2Ti2R5AAVBZoGhRZkReR8uaM9iEEAQjFxPL9CitSuVoAAmGq4ZaNEDNI49PrOTDYbjeerFNYcOFwO4dVQXHbXW5PO7EUwwaBwABGLAghyRx1O7AgKO2OTAROekXJvzEQA) | ||
|
||
---- | ||
|
||
```ts | ||
function eatDinner1() { | ||
const pizza: any = getPizza(); | ||
eatSalad(pizza); | ||
pizza.slice(); | ||
return pizza; // unsafe pizza! | ||
} | ||
|
||
function spiceItUp() { | ||
const pizza = eatDinner1(); | ||
// ^? const pizza: any | ||
pizza.addRedPepperFlakes(); // This call is also unchecked! | ||
} | ||
``` | ||
|
||
[💻 playground](https://www.typescriptlang.org/play/?ts=5.4.5#code/JYOwLgpgTgZghgYwgAgArAF4bsg3ssAewAdjQBzALmQGcwoKBuWgG2CQAoBKagN0OAATZAF8AUKEixEKAMpwWcYfjABPYhGp0GIcs3GCICRVBQwAriARhghEMnIQw6LHG7UX2RmItWbd5Ag4MHlFQQ4aBSVqUKUuPGQAegAqZAA6DORkxNEfS2tbeyCwABFQEGgARm48MWRkBDs6ZDJXajgQVWQAXgcnTzcub3ri2PDW7CG6lsxsNJo2Tin60zBzKHsJuGYknMtImBQtgEIxcTy-QtoyJABJMABVYhrcacaQZq2ewOCykAqoNVlrt6vUAHoAfgaTTAMzayA6qmmWzSSkEACUIIJUBBSNAAGKKADWEBo3B2iRyABUABbAGgNBQsZD0hEsGiEZD5GlGEmCU7iIA) | ||
|
||
---- | ||
|
||
```ts | ||
function eatDinner1() { | ||
const pizza = getPizza(); | ||
// @ts-ignore | ||
eatSalad(pizza); | ||
pizza.slice(); | ||
} | ||
|
||
function eatDinner2() { | ||
const pizza = getPizza(); | ||
// @ts-expect-error | ||
eatSalad(pizza); | ||
pizza.slice(); | ||
} | ||
``` | ||
|
||
[💻 playground](https://www.typescriptlang.org/play/?ts=5.4.5#code/JYOwLgpgTgZghgYwgAgArAF4bsg3ssAewAdjQBzALmQGcwoKBuWgG2CQAoBKagN0OAATZAF8AUKEixEKAMpwWcYfjABPYhGp0GIcs3GCICRVBQwAriARhghEMnIQw6LHG7UX2RmItWbd5Ag4MHlFQQ4aBSVqUKUuPGQAegAqZAA6DORkxNEfS2tbeyCwABFQEGgARm48MWRkBDs6ZDJXZABeBydPNy5vesScgAEwGgBaYHIQQlM6wODY8NbsPrnluDSaNk5V8Ty-QvnS8ugAJhrcOcaQZvWOrudMbG5+pOHRsYgADw1rT6goDM5sVFhx1qt6utNtsIC8xOIgA) | ||
|
||
---- | ||
|
||
```ts | ||
const config: Config = { | ||
a: 1, | ||
b: 2, | ||
c: { | ||
key: value | ||
// ~~~ Property ... missing in type 'Bar' but required in type 'Foo' | ||
} | ||
}; | ||
``` | ||
|
||
[💻 playground](https://www.typescriptlang.org/play/?ts=5.4.5#code/JYOwLgpgTgZghgYwgAgArAF4bsg3ssAewAdjQBzALmQGcwoKBuWgG2CQAoBKagN0OAATZAF8AUKEixEKAMpwWcYfjABPYhGp0GIcs3GCICRVBQwAriARhghEMnIQw6LHG7UX2RmItWbd5Ag4MHlFQQ4aBSVqUKUuPGQAegAqZAA6DORkxNEJcGh4JGQAMUJCBJgyrXomZErCAFknAAtCcJ5kfiFcyQKZZAAhOCgEgCNh6p09HvzpIoBhOxhgcjwxZGQ4ahBzAFtR6G8N0e29g6gj5ARqXHWN5ABrCFVqUsJLkW8DIxMUBDs6J0FOZNINht5-iBAZDllRkIsQLDkABeNYbLbIACMABo7idkAAmXEba5o+5PF5Algg9aJHIAP0ZaCgJGganSmV2wBoNAoyFABHUKAA5EMoMLkKNzGBkKYAI7mYCmYQCtQaZDCt7Cu7iT5iIA) | ||
|
||
---- | ||
|
||
```ts | ||
const config: Config = { | ||
a: 1, | ||
b: 2, | ||
c: { | ||
key: value | ||
} | ||
} as any; // Don't do this! | ||
``` | ||
|
||
[💻 playground](https://www.typescriptlang.org/play/?ts=5.4.5#code/JYOwLgpgTgZghgYwgAgArAF4bsg3ssAewAdjQBzALmQGcwoKBuWgG2CQAoBKagN0OAATZAF8AUKEixEKAMpwWcYfjABPYhGp0GIcs3GCICRVBQwAriARhghEMnIQw6LHG7UX2RmItWbd5Ag4MHlFQQ4aBSVqUKUuPGQAegAqZAA6DORkxNEJcGh4JGQAMUJCBJgyrXomZErCAFknAAtCcJ5kfiFcyQKZZAAhOCgEgCNh6p09HvzpIoBhOxhgcjwxZGQ4ahBzAFtR6G8N0e29g6gj5ARqXHWN5ABrCFVqUsJLkW8DIxMUBDs6J0FOZNINht5-iBAZDllRkIsQLDkABeNYbLbIACMABo7idkAAmXEba5o+5PF5Alggu7iESbGibECqZhJHIAETsAHIwMhBOUwM1gDQAIRiIA) | ||
|
||
---- | ||
|
||
```ts | ||
const config: Config = { | ||
a: 1, | ||
b: 2, // These properties are still checked | ||
c: { | ||
key: value as any | ||
} | ||
}; | ||
``` | ||
|
||
[💻 playground](https://www.typescriptlang.org/play/?ts=5.4.5#code/JYOwLgpgTgZghgYwgAgArAF4bsg3ssAewAdjQBzALmQGcwoKBuWgG2CQAoBKagN0OAATZAF8AUKEixEKAMpwWcYfjABPYhGp0GIcs3GCICRVBQwAriARhghEMnIQw6LHG7UX2RmItWbd5Ag4MHlFQQ4aBSVqUKUuPGQAegAqZAA6DORkxNEJcGh4JGQAMUJCBJgyrXomZErCAFknAAtCcJ5kfiFcyQKZZAAhOCgEgCNh6p09HvzpIoBhOxhgcjwxZGQ4ahBzAFtR6G8N0e29g6gj5ARqXHWN5ABrCFVqUsJLkW8DIxMUBDs6J0FOZNINht5-iBAZDllRkIsQLDkABeNYbLbIACMABo7idkAAmbEbRI5AAqzQgNBQxCgJGgNipm1MtBsLBYV0pCCegju1zR9yeLyBLBBmxomxAqju4k+YiAA) |
Oops, something went wrong.