How to reduce javascript object to only contain properties from interface How to reduce javascript object to only contain properties from interface angularjs angularjs

How to reduce javascript object to only contain properties from interface


It is not possible to do this. The reason being interface is a Typescript construct and the transpiled JS code is empty

//this code transpiles to empty!interface MyInterface {  test: string;}

Thus at runtime there is nothing to 'work with' - no properties exist to interrogate.

The answer by @jamesmoey explains a workaround to achieve the desired outcome. A similar solution I use is simply to define the 'interface' as a class -

class MyInterface {  test: string = undefined;}

Then you can use lodash to pick the properties from the 'interface' to inject into you object:

import _ from 'lodash';  //npm i lodashconst before = { test: "hello", newTest: "world"};let reduced = new MyInterface();_.assign(reduced , _.pick(before, _.keys(reduced)));console.log('reduced', reduced)//contains only 'test' property

see JSFiddle

This is a pragmatic solution that has served me well without getting bogged down on semantics about whether it actually is an interface and/or naming conventions (e.g. IMyInterface or MyInterface) and allows you to mock and unit test


TS 2.1 has Object Spread and Rest, so it is possible now:

var my: MyTest = {test: "hello", newTest: "world"}var { test, ...reduced } = my;

After that reduced will contain all properties except of "test".


Another possible approach:

As other answers have mentioned, you can't avoid doing something at runtime; TypeScript compiles to JavaScript, mostly by simply removing interface/type definitions, annotations, and assertions. The type system is erased, and your MyInterface is nowhere to be found in the runtime code that needs it.

So, you will need something like an array of keys you want to keep in your reduced object:

const myTestKeys = ["test"] as const;

By itself this is fragile, since if MyInterface is modified, your code might not notice. One possible way to make your code notice is to set up some type alias definitions that will cause a compiler error if myTestKeys doesn't match up with keyof MyInterface:

// the following line will error if myTestKeys has entries not in keyof MyInterface:type ExtraTestKeysWarning<T extends never =  Exclude<typeof myTestKeys[number], keyof MyInterface>> = void;//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// Type 'UNION_OF_EXTRA_KEY_NAMES_HERE' does not satisfy the constraint 'never'// the following line will error if myTestKeys is missing entries from keyof MyInterface:type MissingTestKeysWarning<T extends never =  Exclude<keyof MyInterface, typeof myTestKeys[number]>> = void;//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// Type 'UNION_OF_MISSING_KEY_NAMES_HERE' does not satisfy the constraint 'never'

That's not very pretty, but if you change MyInterface, one or both of the above lines will give an error that hopefully is expressive enough that the developer can modify myTestKeys.

There are ways to make this more general, or possibly less intrusive, but almost no matter what you do, the best you can reasonably expect from TypeScript is that your code will give compiler warnings in the face of unexpected changes to an interface; not that your code will actually do different things at runtime.


Once you have the keys you care about you can write a pick() function that pulls just those properties out of an object:

function pick<T, K extends keyof T>(obj: T, ...keys: K[]): Pick<T, K> {  return keys.reduce((o, k) => (o[k] = obj[k], o), {} as Pick<T, K>);}

And them we can use it on your test object to get reduced:

var test: MyTest = { test: "hello", newTest: "world" }const reduced: MyInterface = pick(test, ...myTestKeys);console.log(JSON.stringify(reduced)); // {"test": "hello"}

That works!

Playground link to code