Execute Program

Everyday TypeScript: ReturnType and Parameters

Welcome to the ReturnType and Parameters lesson!

This lesson is shown as static text below. However, it's designed to be used interactively. Click the button below to start!

  • We've seen how to write function types:

  • Here's a code problem:

    This code defines a StringFunction type, but the actual type is missing. Add a type that means "a function that takes no arguments and returns a string".

    type StringFunction = () => string;
    const returnsHello: StringFunction = () => 'hello';
    const hello: string = returnsHello();
    hello;
    Goal:
    'hello'
    Yours:
    'hello'Pass Icon
  • Sometimes we want to extract an existing function's return type or parameter types. TypeScript can do both of those! We can use TypeScript's generic ReturnType type to get the return type. It takes a function type as its type parameter, then gives us the return type of that function.

  • >
    type StringFunction = () => string;
    type OurReturnType = ReturnType<StringFunction>;
    const hello: OurReturnType = 'hello';
    hello;
    Result:
    'hello'Pass Icon
  • >
    type StringFunction = () => string;
    type OurReturnType = ReturnType<StringFunction>;
    const hello: OurReturnType = 123;
    hello;
    Result:
    type error: Type 'number' is not assignable to type 'string'.Pass Icon
  • Note that error message. We may have defined our type as ReturnType<StringFunction>, but it's just an alias for the familiar 'string' type!

  • TypeScript also provides a Parameters type. It takes a function type and gives us a tuple type back. (As a refresher, tuple types are arrays of fixed length.) Each tuple element is one of the function's parameter types, in the same order they were declared.

  • >
    /* Note that DoubleFunction is a type, not a function. It doesn't double
    * numbers. Instead, it says "a function that doubles numbers will return
    * a number". */
    type DoubleFunction = (n: number) => number;
    type DoubleParameters = Parameters<DoubleFunction>;
    const args: DoubleParameters = [5];
    args;
    Result:
    [5]Pass Icon
  • >
    type DoubleFunction = (n: number) => number;
    type DoubleParameters = Parameters<DoubleFunction>;
    const args: DoubleParameters = 5;
    args;
    Result:
    type error: Type 'number' is not assignable to type '[n: number]'.Pass Icon
  • When we use Parameters on a function with multiple parameters, we get a tuple with multiple elements.

  • >
    type AddFunction = (x: number, y: number) => number;
    type AddParameters = Parameters<AddFunction>;
    const args: AddParameters = [1, 2, 3, 4, 5];
    args;
    Result:
    type error: Type '[number, number, number, number, number]' is not assignable to type '[x: number, y: number]'.
      Source has 5 element(s) but target allows only 2.Pass Icon
  • >
    type AddFunction = (x: number, y: number) => number;
    type AddParameters = Parameters<AddFunction>;
    const args: AddParameters = [1, 2];
    args;
    Result:
    [1, 2]Pass Icon
  • We can index into tuple types to get the type of an individual tuple element. When using Parameters, that lets us get the type of just one function argument.

  • Here's a code problem:

    In the code below, we want to create a variable that holds the parameters for an "add" function. However, the type is currently missing. Use the Parameters generic type to get the function type's parameter type, using it as the type for the args variable. (You don't need to modify any of the existing code; you only need to add the missing type.)

    type AddFunction = (x: number, y: number) => number;
    const args: Parameters<AddFunction> = [10, 20];
    args;
    Goal:
    [10, 20]
    Yours:
    [10, 20]Pass Icon
  • What about rest parameters? (As a reminder, rest parameters are a JavaScript and TypeScript feature where functions can take arbitrarily many arguments, which are collected in an array.) When Parameters encounters rest parameters, it sensibly treats them as an array type. For the parameter type of (...args: string[]) => number, it gives us string[].

  • >
    type CountStringsFunction = (...args: string[]) => number;
    type CountParameters = Parameters<CountStringsFunction>;
    const strings: CountParameters = ['a', 'b', 'c'];
    strings;
    Result:
    ['a', 'b', 'c']Pass Icon
  • >
    type CountStringsFunction = (...args: string[]) => number;
    type CountParameters = Parameters<CountStringsFunction>;
    const strings: CountParameters = ['a'];
    strings;
    Result:
    ['a']Pass Icon
  • >
    type CountStringsFunction = (...args: string[]) => number;
    type CountParameters = Parameters<CountStringsFunction>;
    const numbers: CountParameters = [1, 2, 3];
    numbers;
    Result:
    type error: Type 'number' is not assignable to type 'string'.Pass Icon
  • An important note: you've probably heard the terms "parameters" and "arguments" used interchangeably. Then why is this type called Parameters and not Arguments? The reason is that the two words have different meanings when we talk about programming languages in a formal way.

  • A parameter is part of a function's definition, like the n in function double(n: number). An argument is a value that we pass when calling a function, like the 5 in double(5). Many programmers call both of these "arguments" in everyday conversation. Compilers and other programming language tools like TypeScript need to avoid ambiguity, so they usually distinguish between parameters and arguments.

  • TypeScript adds a lot of new syntax like type X = ... and const x: number and Type1 | Type2. It's tempting to think that ReturnType and Parameters are special syntax as well, but they're not! We could write them ourselves if we wanted to. In fact, these types are defined in a regular ".ts" file that comes with TypeScript.

  • If you're curious about how this works, here's the actual definition of ReturnType in our TypeScript installation. (We found this in the file "node_modules/typescript/lib/lib.es5.d.ts".) It uses a few advanced TypeScript features that you may not have seen yet, and utility types tend to be strange at the best of times, so don't worry if it doesn't make sense.

  • >
    type ReturnType<T extends (...args: any) => any> =
    T extends (...args: any) => infer R ? R : any;
  • One thing was conspicuously absent in this lesson. We didn't use Parameters or ReturnType on an actual defined function, like function f(...). Instead, we only used them on function types like () => number. To use these types on actual functions, we'll need the typeof operator. We cover that topic in our Advanced Typescript course, but we strongly recommend finishing this course before moving on to Advanced TypeScript!