https://kentcdodds.com/blog/typescript-function-syntaxes
// Simple type for a function, use =>
type FnType = (arg: ArgType) => ReturnType
// Every other time, use :
type FnAsObjType = {
(arg: ArgType): ReturnType
}
interface InterfaceWithFn {
fn(arg: ArgType): ReturnType
}
const fnImplementation = (arg: ArgType): ReturnType => {
/* implementation */
}
type MathFn = {
(a: number, b: number): number
operator: string
}
const sum: MathFn = (a, b) => a + b
sum.operator = '+'
This can also be done with an interface:
interface MathFn {
(a: number, b: number): number
operator: string
}
const sum: MathFn = (a, b) => a + b
sum.operator = '+'
And then there's declare function
and declare namespace
:
declare function MathFn(a: number, b: number): number
declare namespace MathFn {
let operator: '+'
}
const sum: typeof MathFn = (a, b) => a + b
sum.operator = '+'
Object method:
const math = {
sum(a: number, b: number): number {
return a + b
},
}
Property as function expression:
const math = {
sum: function sum(a: number, b: number): number {
return a + b
},
}
Property as arrow function expression (with implicit return):
const math = {
sum: (a: number, b: number): number => a + b,
}
Unfortunately, to extract the type you can't type the function itself, you have to type the enclosing object. You can't annotate the function with a type by itself when it's defined within the object literal:
type MathFn = (a: number, b: number) => number
const math: {sum: MathFn} = {
sum: (a, b) => a + b,
}
Furthermore, if you want to add a property on it like some of the above examples, that is impossible to do within the object literal. You have to extract the function definition completely:
type MathFn = {
(a: number, b: number): number
operator: string
}
const sum: MathFn = (a, b) => a + b
sum.operator = '+'
const math = {sum}
You may have noticed that this example is identical to an example above with only the addition of the const math = {sum}. So yeah, there's no way to do all this inline with the object declaration.
interface MathUtilsInterface {
sum(a: number, b: number): number
}
class MathUtils implements MathUtilsInterface {
sum(a: number, b: number): number {
return a + b
}
}
Here's what we'd do assuming we export it as the default:
declare const sum: {
(a: number, b: number): number
operator: string
}
export default sum
And if we want it to be a named export:
declare const sum: {
(a: number, b: number): number
operator: string
}
export {sum}
const sum = async (a: number, b: number): Promise => a + b
async function sum(a: number, b: number): Promise<number> {
return a + b
}
type FalsyType = false | null | undefined | '' | 0
function typedBoolean<ValueType>(
value: ValueType,
): value is Exclude<ValueType, FalsyType> { // 👈
return Boolean(value)
}
type User = {
name: string
displayName: string | null
}
function assertDisplayName(
user: User,
): asserts user is User & {displayName: string} {
if (!user.displayName) throw new Error('Oh no, user has no displayName')
}
function logUserDisplayName(user: User) {
assertDisplayName(user)
console.log(user.displayName.toUpperCase())
}