M.K. Safi
M.K. Safi

Reputation: 7019

Strictly define the type of a function that takes an object, modifies its values, and returns it in TypeScript

I'd like to create a function that makes a new object with the same keys as the original object it received but with values equal to null.

So...

function nullify(arg) {
  var returnObj = {}

  for (var key in arg) {
    returnObj[key] = null
  }

  return returnObj
}

I would like to add types to this function so that the TypeScript compiler will give a warning when I try to access a property that doesn't exist on the returned object. For example:

var nulled = nullify({
  a: 1,
  b: 2
})

console.log(nulled.foobar) // Error `foobar` doesn't exist.

Is something like that possible? Perhaps by using generics and keyof?

Sorry, a complete TypeScript noob here

Upvotes: 2

Views: 84

Answers (2)

NineBerry
NineBerry

Reputation: 28499

You can do this by using generics like this:

function nullify<T>(arg: T): T {
    var returnObj = {} as T;

    for (var key in arg) {
        returnObj[key] = null
    }

    return returnObj
}

Usage:

var nulled = nullify({
    a: 1,
    b: 2
})

console.log(nulled.a); // No problem
console.log(nulled.x); // Compiler error  Property 'x' does not exist on type '{ a: number; b: number; }'.

Note that you do not need to specify the generic type when calling the function. The type is automatically derived by the compiler from the argument passed to the function.


Extending and correcting Paarth's answer: When using strict null checking and having TypeScript 2.1 or higher available, you might want to declare the returned type to allow null values on its properties:

// Defined mapped type Nullified from type T.
// Mapped type has all the same properties with the 
// same types except that the properties in Nullified
// can also be null
type Nullified<T> = {
    [P in keyof T]: T[P] | null
}

function nullify<T>(arg: T): Nullified<T> {
    var returnObj = {} as Nullified<T>;

    for (var key in arg) {
        returnObj[key] = null
    }
    return returnObj
}

Upvotes: 2

Paarth
Paarth

Reputation: 10377

When using TS 2.1 you can use mapped types. By default a type is typically actually type | number. However, if you have strict checking for null objects using mapped types is the more descriptive approach.

type Nullified<T> = {
    [P in keyof T]?: null
}

function nullify<T>(arg:T) : Nullified<T> {
  var returnObj = {}

  for (var key in arg) {
    returnObj[key] = null
  }

  return returnObj
}

Upvotes: 5

Related Questions