- Published on
Day19: Learning TypeScript: More on Functions
- Authors

- Name
- irisjustdoit
- @irisjustdoit
Day19: Learning TypeScript: More on Functions
We briefly discussed functions earlier in Day09; this article will explore some deeper applications of functions.
Using the Function Keyword
The simplest way to define a function:
function greeter(fn: (a: string) => void) {
fn("Hello, World");
}
function printToConsole(s: string) {
console.log(s);
}
greeter(printToConsole); // Hello, World
(a: string) => void indicates that this function takes one parameter named a, which is of type string, and this function does not return a value.
It is important to note that the parameter must be named, such as (a: string). If it is not named, writing (string) => void would indicate that the parameter is named string, and its type would be any.
Additionally, types and interfaces can also define functions; refer to Day15 for more information.
Call Signatures: Adding Properties to Functions
Initially, I really didn't understand this; thanks to Google, I found a way to implement the official example and got a rough understanding. Below, I will implement it.
In JavaScript, functions can have properties added to them (though I haven't tried adding properties to functions). So I wanted to prove whether it is indeed possible, and I wrote a small example, and it turns out it is indeed possible:
const jsFunc = () => {
return "this's a function";
}
jsFunc.prop = "this's a prop";
jsFunc.desc = "this's a desc";
console.log(jsFunc); //[Function: func] { prop: "this's a prop", desc: "this's a desc" }
However, in TypeScript, type expressions do not allow adding properties to functions, so we can write a call signature in an object type, such as DescribableFunction. Note that the return type is specified using :, not =>.
// Adding call signatures
type DescribableFunction = {
description: string;
(someArg: number): boolean; // Note that we use : instead of =>
};
function doSomething(fn: DescribableFunction) {
console.log(fn.description + " returned " + fn(6));
}
// Returns boolean if greater than 5
const func = (someArg: number): boolean => {
return someArg > 5;
};
// Adding properties
func.description = "isNumber > 5";
doSomething(func); // isNumber > 5 returned true
Construct Signatures
Generally, we use new to create a new object, which is called constructors.
type SomeConstructor = {
new (s: string): SomeObject;
};
function fn(ctor: SomeConstructor) {
return new ctor("hello");
}
Optional Parameters
Although in JavaScript, parameters not passed to a function will be undefined and can work normally, in TypeScript, function parameters are required by default. If not provided, the compiler will throw an error.
Similar to optional properties in object types, parameters can also be optional by using ? after the parameter.
// firstName is required, lastName is optional
const getName = (firstName: string, lastName?: string) => {
return lastName ? `${firstName} ${lastName}` : firstName;
}
⚠️ It is important to note that optional parameters must always be placed after required parameters.
⚠️ The official documentation also specifically advises against using optional parameters in callbacks, as it may lead to this kind of error.
Default-Initialized Parameters
To use default values for parameters:
const getName = (firstName: string, lastName = "Chen") => {
return `${firstName} ${lastName}`;
}
console.log(getName("Tom")); // Tom Chen
If you want to use a default value, you need to use undefined when calling the function.
const getName = (firstName = "Tom", lastName: string) => {
return `${firstName} ${lastName}`;
}
console.log(getName(undefined, "Chen")); // Tom Chen
Rest Parameters
To add an unlimited number of parameters, you can use the rest parameter syntax ....
function multiply(n: number, ...m: number[]) {
return m.map((x) => n * x);
}
const a = multiply(10, 1, 2, 3, 4);
console.log(a); //[10, 20, 30, 40]
const getName = (firstName: string, ...rest: string[]) => {
return `${firstName} ${rest.join(' ')}`;
}
const names = getName('Tom', 'Jerry', 'Chen');
console.log(names);
Rest Arguments
While it is fine to add remaining parameters using push:
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
arr1.push(...arr2);
console.log(arr1); // [1, 2, 3, 4, 5, 6]
In TypeScript, there can be issues with usage: args will be inferred as number[], not specifically two numbers.
// Inferred type is number[] -- "an array with zero or more numbers",
// not specifically two numbers
const args = [8, 5];
// error
const angle = Math.atan2(...args);
For more information on Math.atan2, refer here.
We can treat the array as a tuple by adding as const when defining the array, making it a fixed-value readonly array, which resolves the issue. If as const is not added, it will be number[]:
// Inferred as 2-length tuple
const args = [8, 5] as const;
// ok
const angle = Math.atan2(...args);
In the next article, I will take notes on Function Overloads. I just learned that this feature exists, and I think it's very cool.
References
https://zhuanlan.zhihu.com/p/266823134 https://stackoverflow.com/questions/66874130/how-to-properly-use-functions-construct-signatures-in-typescript https://basarat.gitbook.io/typescript/type-system/type-inference https://pjchender.dev/typescript/ts-functions/#%E9%81%B8%E5%A1%AB%E6%80%A7%E7%9A%84%E5%8F%83%E6%95%B8%E8%88%87%E5%8F%83%E6%95%B8%E9%A0%90%E8%A8%AD%E5%80%BC https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Math/atan2 https://pjchender.dev/typescript/ts-basic-types/
