Master Advanced TypeScript: Boost Code Safety and Maintainability
This comprehensive guide explores advanced TypeScript concepts—including basic and advanced types, function signatures, type inference, compatibility, generics, modules, declaration files, and compilation options—to help developers write safer, more maintainable code in modern front‑end projects.
Preface
In 2020 TypeScript (TS) has become essential for both server‑side (Node.js) and front‑end frameworks (Angular, Vue3). This article introduces several advanced TS techniques to deepen your understanding of the language.
TypeScript Overview
Superset of ECMAScript (stage 3)
Compile‑time type checking
Zero runtime overhead (no extra dependencies, no syntax extensions)
Compiles to clean, readable JavaScript
TypeScript = Type + ECMAScript + Babel‑Lite
Design goals: TypeScript Design Goals
Why Use TypeScript
Improves code readability and maintainability
Reduces runtime errors, making code safer
Provides intelligent code suggestions
Powerful refactoring support
Basic Types
boolean
number
string
array
tuple
enum
void
null & undefined
any & unknown
never
any vs unknown
any: any type unknown: unknown type
Any type can be assigned to unknown, but unknown cannot be assigned to other specific types, while any can be assigned to or from anything.
let foo: unknown
foo = true // ok
foo = 123 // ok
foo.toFixed(2) // error
let foo1: string = foo // error let bar: any
bar = true // ok
bar = 123 // ok
foo.toFixed(2) // ok
let bar1: string = bar // okUsing any discards type checking, so it should be avoided. Prefer unknown for values of uncertain type.
Correct usage of unknown
You can narrow unknown to a more specific type using type guards:
function getLen(value: unknown): number {
if (typeof value === 'string') {
// value is treated as string here
return value.length
}
return 0
}This process is called type narrowing.
never
neverrepresents a type that cannot be reached. In TS 3.7 the following code errors:
// never user‑controlled flow analysis
function neverReach(): never {
throw new Error('an error')
}
const x = 2
neverReach()
x.toFixed(2) // x is unreachable nevercan also be used as the “bottom” type in union types:
type T0 = string | number | never // T0 is string | numberFunction Types
Writing return‑type annotations
function fn(): number {
return 1
}
const fn = function(): number {
return 1
}
const fn = (): number => {
return 1
}
const obj = {
fn(): number {
return 1
}
}Add the return type after the parentheses.
Function type syntax
In TS you can describe a function type:
type FnType = (x: number, y: number) => numberFull function declaration
let myAdd: (x: number, y: number) => number = function(x, y) {
return x + y
}
// Using the type alias
let myAdd: FnType = function(x, y) {
return x + y
}
// TS can infer parameter types
let myAdd: FnType = function(x, y) {
return x + y
}Function overloads
TS supports overload signatures followed by a single implementation signature:
// overload signatures
function toString(x: string): string;
function toString(x: number): string;
// implementation
function toString(x: string | number) {
return String(x)
}
let a = toString('hello') // ok
let b = toString(2) // ok
let c = toString(true) // errorThe implementation signature is not visible to callers.
function toString(x: string): string;
function toString(x: number): string;
function toString(x: string | number) {
return String(x)
}When multiple overload signatures exist, they are not merged; the implementation must be compatible with all overloads.
Type Inference
TS has powerful type inference:
Basic inference : let x = 10 → number Object inference :
const myObj = { x: 1, y: '2', z: true }
// myObj: { x: number; y: string; z: boolean; }Function return inference :
function len(str: string) {
return str.length
}
// return type inferred as numberContextual inference :
const xhr = new XMLHttpRequest()
xhr.onload = function(event) {}For simple values you can often omit explicit type annotations and let TS infer them.
Type Compatibility
TS uses structural subtyping (duck typing). If the structure matches, the types are compatible.
class Point { x: number }
function getPointX(point: Point) { return point.x }
class Point2 { x: number }
let point2 = new Point2()
getPointX(point2) // OKStatic languages like Java or C++ use nominal subtyping, requiring explicit inheritance declarations.
public class Main {
public static void main(String[] args) {
getPointX(new Point()); // ok
getPointX(new ChildPoint()); // ok
getPointX(new Point1()); // error
}
public static void getPointX(Point point) {
System.out.println(point.x);
}
static class Point { public int x = 1; }
static class Point2 { public int x = 2; }
static class ChildPoint extends Point { public int x = 3; }
}Object subtypes
A subtype must contain all properties of the source type:
function getPointX(point: { x: number }) { return point.x }
const point = { x: 1, y: '2' }
getPointX(point) // OKPassing an object literal directly triggers an excess property check and results in an error.
getPointX({ x: 1, y: '2' }) // errorFunction subtypes – contravariance and covariance
Parameter types are checked contravariantly, return types covariantly. The article demonstrates this with classes Animal, Dog, and WangCai.
Advanced Types
Union and Intersection Types
Union ( |) represents "or", intersection ( &) represents "and".
function genLen(x: string | any[]) {
return x.length
}
interface Person { name: string; age: number }
interface Animal { name: string; color: string }
const x: Person & Animal = { name: 'x', age: 1, color: 'red' }Using unions as enums avoids runtime enum overhead.
type Position = 'UP' | 'DOWN' | 'LEFT' | 'RIGHT'
const position: Position = 'UP'Type Guards
Common pattern for narrowing unknown types:
function isString(value: unknown): value is string {
return Object.prototype.toString.call(value) === '[object String]'
}
function fn(x: string | number) {
if (isString(x)) {
return x.length // x is string here
} else {
// x is number here
}
}Other guards: typeof, instanceof, truthy checks for null/undefined, and non‑null assertion operator !.
Key Type Operators
typeof(type query) keyof – gets a union of property names in – iterates over a union of keys in mapped types
Indexed access
T['prop']Mapped Types and Utility Types
Example of a custom mapped type Copy<T> that copies all properties.
type Copy<T> = { [K in keyof T]: T[K] }Built‑in utilities include Partial, Readonly, Pick, Exclude, Extract, NonNullable, ReturnType, InstanceType, etc.
Conditional Types and Distribution
Syntax: T extends U ? X : Y. When T is a union, the condition distributes over each member.
type IsNumber<T> = T extends number ? true : false
type X = IsNumber<string> // false
type Union = string | number
type UnionResult = IsNumber<Union> // true | falseInfer Keyword
infercaptures a type within a conditional. Used by ReturnType:
type ReturnType<T> = T extends (...args: any) => infer R ? R : never
type Fn = (str: string) => number
type FnReturn = ReturnType<Fn> // numberModules
Global vs File Modules
Code without import or export lives in the global module. Adding an import or export turns the file into a module with its own scope.
Module Resolution Strategies
TS supports Node and Classic resolution. The module compiler option (e.g., es2015, commonjs) influences the default strategy; it can also be forced with moduleResolution.
Classic resolves by searching upward directories for matching files; Node follows the Node.js node_modules hierarchy.
Declaration Files
Declaration files ( .d.ts) describe the shape of JavaScript libraries, providing type information for editors.
Editors locate a library’s declaration file by looking for index.d.ts in the package root, or by reading the types / typings field in package.json. If a library lacks its own typings, you can install them from the @types repository (e.g., npm i @types/lodash).
You can also write your own declarations:
declare module 'lodash' {
export function chunk(array: any[], size?: number): any[]
export function get(source: any, path: string, defaultValue?: any): any
}Extending global objects (e.g., window) is done by augmenting the interface in a .d.ts file:
interface Window { myprop: number }
// Now window.myprop is typed.To augment third‑party modules, use declare module 'module-name':
declare module 'vue/types/vue' {
interface Vue { myprop: number }
}Handling non‑JS assets:
declare module '*.vue' {
import Vue from 'vue'
export default Vue
}
declare module '*.css' {
const content: any
export default content
}Compilation
The TypeScript compiler ( tsc) converts .ts files to JavaScript according to tsconfig.json options.
target : ECMAScript version (e.g., ES5, ES2017)
module : module system (e.g., commonjs, es2015, umd)
lib : ambient declarations for the target environment (e.g., DOM, ES2015)
Common options:
{
"compilerOptions": {
"module": "umd",
"outDir": "./dist"
}
}Use outFile to bundle all output into a single file for AMD or System modules. For UMD bundles, combine tsc with a bundler like Rollup or Webpack:
// rollup.config.js
const typescript = require('rollup-plugin-typescript2')
module.exports = {
input: './index.ts',
output: {
name: 'MyBundle',
file: './dist/bundle.js',
format: 'umd'
},
plugins: [typescript()]
}Useful TypeScript Ecosystem Packages
@typescript-eslint/eslint-plugin & @typescript-eslint/parser – linting
DefinitelyTyped – @types repository
ts-loader, rollup-plugin-typescript2 – bundler integrations
typedoc – API documentation generator
typeorm – TypeScript‑first ORM
nest.js, egg.js – server‑side frameworks with TS support
ts-node – run TS directly with Node
utility-types – handy type utilities
type-coverage – static type‑coverage analysis
Tip: Path Alias in VS Code
Configure baseUrl and paths in tsconfig.json (or jsconfig.json) to get autocomplete and navigation for aliases like @/utils. WebStorm users can set the Webpack config path in settings for the same effect.
Learning Resources
TypeScript 中文网
TypeScript 入门教程
GitHub – awesome‑typescript
知乎专栏 – 来玩 TypeScript 啊,机都给你开好了!
conditional‑types‑in‑typescript (TS conditional types)
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
WeDoctor Frontend Technology
Official WeDoctor Group frontend public account, sharing original tech articles, events, job postings, and occasional daily updates from our tech team.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
