Skip to main content

Advanced usage of TypeScript(一)

· 5 min read

This article introduces the advanced uses of TypeScript. For those who are familiar with TypeScript or have been using TypeScript for some time, it introduces TypeScript from the perspectives of types, operators, generics.



unknown refers to a type that cannot be defined in advance. In many scenarios, it can replace the functionality of any while preserving the ability to statically check.

const num: number = 10;
(num as unknown as string).split('');

Note that static checks are just as valid here as any. You can cast it to any type. The difference is that when compiled statically, unknown cannot call any method, whereas any can.

const foo: unknown = "string";
foo.substr(1); // Error: static check does not pass error
const bar: any = 10;
any.substr(1); // Pass: skip static checking

one use case of unknown is to avoid the static type checking bug caused by using any as the parameter type of a function:

function test(input: unknown): number {
if (Array.isArray(input)) {
return input.length; // Pass: The type guard has identified the input as an array type
return input.length; // Error: input is of unknown type. static check error


In TypeScript, void has a similar to undefined, which logically avoids the error of accidentally using a null pointer.

function foo() {} // The default return type is void
const bar = foo(); // bar's type is defined as void, and you can't call any of property methods of bar

The biggest difference between void and undefined is that you can think of undefined as a subset of void, and use void instead of undefined when you don't care about the return value of a function. Take a practical example from React.

// Child.tsx
type Props = {
getValue: () => void; // number、string、undefined etc.
function Child({ getValue }: Props) => <div>{getValue()}</div>
// Parent.tsx
function Parent(): JSX.Element {
const getValue = (): number => {
return 2;
}; /* number type */
// const getValue = (): string => { return 'str' }; /* string type also enable to pass child */
return <Child getValue={getValue} />;


never is a type that cannot be returned normally, as a function that is bound to report an error or an infinite loop returns.

function foo(): never {
throw new Error("error message");
} // throw error return type is never
function foo(): never {
while (true) {}
} // infinite loop return type is never
function foo(): never {
let count = 1;
while (count) {
} // Error: it is not directly recognized at static compilation time.

There are also types that never intersect:

type human = "boy" & "girl"; // human is never type

Any type combined with the never type remains the same:

type language = "javascript" | never; // language is string type

never has the following features

  • After you call a function that returns never in a function, all subsequent code becomes deadcode.
function foo(): never {
throw new Error("error message");

function test() {
console.log(111); // Error
  • No other type can be assigned to never.
let n: never;
let o: any = {};
n = o; // Error: cannot assign a non-never type to a never type, including any


not null assertion operator !

It helps the compiler that I'm sure this is not a null or undefined variable.

let foo: { bar: string  } | null | undefined;
// the baz type is string
let baz = foo!.bar; // we tell compiler we are sure foo is not null & not undefined so the type of baz is string

optional chaining

We can use ?. for safe reading and deleting, but not writing.

obj?.prop // object
obj?.[0] // array
func?.(args) // function

Let's say we write a?.b, the compiler automatically generates the following code.

a === null || a === void 0 ? void 0 : a.b;

nullish coalescing operator ??

?? is used to return the first non-null (not null or not undefined) value.

const nullValue = null;
const emptyText = ""; // falsy
const someNumber = 42;

const valA = nullValue ?? "default for A"; // "default for A"
const valB = emptyText ?? "default for B"; // "default for B"
const valC = someNumber ?? 0; // 42


keyof can take all the keys of a type and return a string or numeric literal union of its keys.

type Person = {
name: string;
age: number;
type PersonKey = keyof Person; // "name" | "age"

typeof can also get the typeof an object/instance

const me: Person = { name: 'jstyro', age: 16 };
type P = typeof me; // { name: string; age: number; }
const you: typeof me = { name: 'jstyro-two', age: 69 } //