2

I'm going to answer my own question here because this is pretty difficult to Google and I want it to be easier for everyone to find.

This article explains what this pattern is:

An “options object” is a programming pattern that you can use to pass any number of named arguments to a function. Rather than using an ordered list of arguments, you simply pass one argument, which is an object containing named keys for all of your options. Keep reading to learn how and why.

You can use typescript features to implement this pattern in a more concise way. See my answer for how.

You can see a similar question about JavaScript here: Implement JavaScript options object parameter pattern?

This question is different because it's about TypeScript. There happens to be an answer on that JavaScript question that explains how to do it the typescript way, but I think it deserves its own question.

2 Answers 2

1

100% Optional

This is how, using destructuring, you can use the options object pattern in typescript (playground link):

    interface Options {
      lastSeparator?: string;
      separatorBetweenArrayOfTwo?: string;
      wrap?: (word: string) => string;
    }
    
    const separatedMessage = (
      words: string[],
      separator: string,
      { 
        lastSeparator = separator, 
        separatorBetweenArrayOfTwo = lastSeparator, 
        wrap = (word: string): string => {
          return word.toString();
        }
       }: Options = {} // the `= {}` at the end lets you skip this arguments completely. e.g., `separatedMessage(["a"], " ");` will compile. Without `= {}`, you would have to call it like this to compile: `separatedMessage(["a"], " ", {});`
    ): string => {
      let buf = '';
    
      words.forEach((word, index, array) => {
        if (index !== 0) {
          if (array.length === 2) {
            buf += separatorBetweenArrayOfTwo;
          } else if (index === array.length - 1) {
            buf += lastSeparator;
          } else {
            buf += separator;
          }
        }
          buf += wrap(word);    
      });
      return buf;
    };
    
    console.log(separatedMessage(["a", "b", "c", "d"], ", "))
    console.log(separatedMessage(["a", "b"], ", ", {}))
    console.log(separatedMessage(["a", "b"], ", ", {lastSeparator: ", and "}))
    console.log(separatedMessage(["a", "b"], ", ", {lastSeparator: ", and ", separatorBetweenArrayOfTwo: " and "}))

This function lets you pass in an array of words and separate them with characters. You can optionally:

  1. Specify a different separator to use last: , and in a, b, c, and d.
  2. Specify a separator that's used for arrays of exactly two elements: and in a and b but , and in a, b, c, and d.
  3. Pass in a function that wraps each word in a string: ' in 'a', 'b', 'c', and 'd'

Some required properties

Playground link

type Options = {
  r1: string;
  o2?: number;
  o3?: string;
  r4: number;
};

foo('qux', {
  r1: 'baz',
  r4: 3,
});

function foo(r0: string, { r1, o2 = 6, o3 = 'bar', r4 }: Options) {
  console.log(`r0=${r0} r1=${r1} o2=${o2} o3=${o3} r4=${r4}`); // r0=qux r1=baz o2=6 o3=bar r4=3
}

/*
foo('qux', {
    r4: 3,
});

Error: Argument of type '{ r4: number; }' is not assignable to parameter of type 'Options'.
  Property 'r1' is missing in type '{ r4: number; }' but required in type 'Options'.
*/
Sign up to request clarification or add additional context in comments.

Comments

0

We can define specific key-value pair or any for interface, then destructing the values.

interface Person {
    name: string;
    age: number;
    gender: string;
    hobby?: string;
    [key: string]: any;
}

function logPerson(person: Person) {
    console.log(person);
    return person;
}

function logPerson1(person: { [key: string]: any }) {
    console.log(person);
}

console.clear();
logPerson({ name: 'Kevin', age: 33, gender: 'male', hobby: 'basketball', hah: 'Hah' });
logPerson1({ name: 'Lucy', age: 100, gender: 'female', extra: 'extra property' });

3 Comments

I don't think that's as good because it isn't type safe. Still useful to know
actually I'm not even sure if this answer is relevant.how is this related to the optional object pattern? Nothing is defaulting
Yes, I think the definition of 'options object' patten you mentioned will not be type safe in this situation, unless you can define the actually structures for your 'options', otherwise only can use it with {[key: string]}: any

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.