10

I have the following initial object

const initialValues = { name: 'Jon', email: '[email protected]' }

I would like to create an identical object except where all the values are boolean and default to false. Like so

const expected = { name: false, email: false }

I created the following function which does what I want

const expected = cloneWithDefaults<typeof initialValues>(initialValues)

function cloneWithDefaults<T>(values: T) {
  type U = { [K in keyof T]: boolean }
  const keys = Object.keys(values) as Array<keyof T>
  const partial: Partial<U> = keys.reduce<Partial<U>>((acc, cur) => {
    acc[cur] = false
    return acc
  }, {})
  const final = partial as U
  return final
}

I also created a second version which does not use reduce

function createCloneWithDefaultsV2<T extends { [key: string]: unknown }>(values: T) {
  type U = { [K in keyof T]: boolean }
  const keys = Object.keys(values) as Array<keyof U>
  const partial: Partial<U> = {}
  for (let k in keys) {
    partial[k] = false
  }
  const final = partial as U
  return final
}

I wonder if there is a better / more succinct way of doing this. In particular I would like to get rid of the two uses of as if possible.

In the v2 I wonder if there is any preference for unknown as opposed to any.

3
  • 1
    What's your problem with unknown? You don't use the value in your function and there seems to be no requirement to limit the type of your values in any way. So if you want to accept any type an this place, any and unknown should be ok here. (afaik there's no difference here because you do not access the value) Commented May 11, 2019 at 13:51
  • I could use any as opposed to unknown just curious if there is a preference in this scenario. Commented May 11, 2019 at 13:57
  • 1
    Unkown vs. any: stackoverflow.com/questions/51439843/unknown-vs-any Commented May 11, 2019 at 14:01

4 Answers 4

8

Here's how I would write it:

    function cloneWithDefaults<T>(values: T) {
        return <{[key in keyof T]: boolean}> Object.entries(values).reduce((p, [k, v]) => Object.assign(p, { [k]: false }), {});
    }
    const initialValues = { name: 'Jon', email: '[email protected]' };
    const expected = cloneWithDefaults(initialValues);

Due to the reduce() call, I don't see an option without a cast. (Either from partial or from any.) Note that Object.entries() requires downlevelIteration ts compiler option to be true (https://www.typescriptlang.org/docs/handbook/compiler-options.html).

Sign up to request clarification or add additional context in comments.

Comments

2

Here is another alternative that builds on Christoph's answer:

const cloneWithDefaultValues = <T>(input: T) => Object
    .keys(input)
    .reduce(
        (clone, key) => ({ [key]: false, ...clone }),
        {} as Record<keyof T, boolean>
    );

2 Comments

Would still need a final as {[key in keyof T]: boolean}
@david_adler Why do we need a final as {[key in keyof T]: boolean}. The current version works in the playground, to which there is a link in the answer.
0

You could also write a class with default values, so that:

class YourObject {
    name: string | boolean = false;
    email: string | boolean = false;

    constructor(
        name: string | boolean = false,
        email: string | boolean = false
    ) {
        this.name = name;
        this.email = email;
    }
}

// now, you can create an object with no parameters:

let obj = new YourObject();
console.log(obj);
// output:
//      Object { name: false, email: false }

// then assign whatever strings to its fields:
obj.name = "Jon";
obj.email = "[email protected]";

// or create a new one with known values:
let jon = new YourObject("Jon", "[email protected]");
console.log(jon);
// output:
//      Object { name: "Jon", email: "[email protected]" }

Comments

0

This might be an alternative which uses only one as

function cloneWithDefaults<T>(values: T) {
     const exp: {[key in keyof T]: boolean} = Object.entries(values).reduce((p, [k, v]) => Object.assign(p, { [k]: false }), {} as {[key in keyof T]: boolean});
        return  exp;
    }
    const initialValues = { name: 'Jon', email: '[email protected]' };
    const expected = cloneWithDefaults(initialValues);
    console.log(">>>>>>", expected)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.