TransWikia.com

Specifying function return type with certain constraints applied

Stack Overflow Asked on December 30, 2021

I’m new to TypeScript and struggling to achieve something or even know if it is possible.

I want to be able to define a type for a function which allows instances of the function to set their own return type but at the same time restricts that return type to being an object where the keys are strings and the values are strings.

So the generic type definition should allow any return type matching { [key: string]: string } but a specific instance of the function could specify that it will return { a: ‘y’, b: ‘z’ } and then when using the returned value of the function the compiler will know that it will only have properties a and b and not just any string properties.

type obj = {
  name: string,
  doAThing: (path: string) => { [key: string]: string }
}

const anObj: obj = {
  name: 'Alias Fakename',
  doAThing: path => ({ a: 'abc', b: 'xyz' })
}

const result = anObj.functionInstance('abcdef')

In the above example doAThing in anObj is a valid implementation of genericFunction. But when using result the compiler does not know that it will only have properties a and b and allows any string key to be used e.g. result.keyWhichDoesntExist does not throw a type error.

Is there a way when defining doAThing in anObj to specify its return type so that type hinting will work on result whilst ensuring that the specified return type conforms to the return type of genericFunction?

One Answer

You want to make your function type generic with the <T> syntax:

type GenericFunction<T extends { [key: string]: string }> = (path: string) => T

This function type accepts another type T that must have string key names and string values, and whatever that type is, that is what the function returns.

You can then use that type and lock in the type it operates on like so:

const functionInstance: GenericFunction<{ a: string, b: string }> =
  (path: string) => ({ a: 'abc', b: 'xyz' })

Now when you use that function, typescript knows the return type is { a: string, b: string }.

const result = functionInstance('a/path/here')
console.log(result.a) // 'abc'
console.log(result.b) // 'xyz'

Playground link

You can read more about generics in the handbook.


So this function is part of an interface?

Then you can make the interface itself generic, and do pretty much the same thing:

type MyObj<T extends { [key: string]: string }> = {
  name: string,
  doAThing: (path: string) => T
}

Now MyObj accepts the T generic parameter, and doAThing() just uses it.

You would create a MyObj like this:

const anObj: MyObj<{ a: string, b: string }> = {
  name: 'Alias Fakename',

  // doAThing() return type here is checked to match T
  doAThing: path => ({ a: 'abc', b: 'xyz' }) 
}

And now the return value is strongly typed:

const result = anObj.doAThing('abcdef')
console.log(result.a) // works
console.log(result.b) // works

Playground

Answered by Alex Wayne on December 30, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP