TypeScript really does amaze me with how much itâs managed to bring order to the organically-grown, deliberately-permissive, and ad-hoc overloaded APIs of the JavaScript ecosystems, some of them more than 20 years old. Type narrowing supports the idioms that, for better or worse, have emerged as the, well, idiomatic way to define and use such APIs. And the fact that theyâve managed to do so performantly is a marvel.
But C and Objective-C and Swift APIs usually arenât like that. C and Objective-C donât have overloading, but their type systems are strong enough to discourage the sort of ad-hoc overloading JavaScript does instead (and by extension TypeScript).* Swift does have overloading, and doesnât need to do it ad-hoc at all.
As people have noted, the two main uses for narrowing that apply to Swift are checking Optionals and downcasting. In TypeScript these are both narrowing operations because both are usually represented as taking an anonymous union type and ruling out certain arms of that union. On the flip side, TypeScript tries to preserve JavaScript idioms, so having to convert if x === null to guard const x else would mean fewer people would adopt TypeScript. But neither of those apply to Swift, because Swift did not need to stick as closely to existing syntax, and because Swift doesnât have anonymous union types. Instead, Swift encourages protocols for factoring out common operations across disparate types, rather than type-switches. Thatâs a choice with trade-offs, to be sure, but itâs more feasible in Swift than JavaScript because extension methods on Swift types canât collide at run-time.** So no âprototype pollutionâ.
Of course, Swift does still think this is useful for Optionals and for enums in general, which is why if let/guard let and switch exist. And to be fair, TypeScriptâs system can represent some checks that Swiftâs canât: if x.y !== null has no direct equivalent in Swift for further uses of x, and similarly thereâs no way to represent the resulting type X & { y: Y } (rather than y: Y | null) in todayâs Swift. But on the flip side, Swift lets you add methods and protocols to the built-in String and Int types, so thereâs much less need for common unions like string | number.
* At least with the way Apple has used Objective-C. The original ObjC had sensibilities closer to Smalltalkâs and thus to JavaScriptâs, with id-typed parameters and a general expectation of âduck typingâ; if NeXT had stayed in that vein, we might have had a different world. But even duck typing is closer to TypeScriptâs interfaces than its union types.
** The exception is methods exposed to Objective-C, whose dispatch model is closer to JavaScriptâs.