Methods usage analysis

The String primitive wrapper object is selected to research how some of its methods perform on the instance to understand intuitiveness and consistency in native objects. Below are possible ways methods work on an instance broken down into nine approaches, and each contains a list of words picked from examples for a better understanding of the naming methodology and further analysis.

Primitive value part

The text contains the keyword 'part of the primitive value', and the following example clarifies its meaning.

// Define a new `Text` class.
class Text extends string {
  constructor(
    public prefix: string, // Part of the primitive value.
    public text: string,  // Part of the primitive value.
    public suffix: string // Part of the primitive value.
  ) {
    super(`${prefix}${text}${suffix}`); // The primitive value.
  }
}

// Returns <span>
new Text('<', 'span', '>').valueOf();

The primitive value consists of prefix, text, and suffix properties, and each of them is a part of the primitive value.

Approaches

One

The method without parameters gets the primitive value and returns a primitive value.

There are two valueOf() and toString() methods that match the description.

// Define a new `String` object.
const text = new String('text');

// Returns 'text';
text.valueOf();

// Returns 'text';
text.toString();

Words

Two

The method without parameters gets the primitive value and performs an action on it based on the intuitive method name, and returns the changed primitive value.

There are among others big(), blink(), bold(), fixed(), italics(), small(), strike(), sub(), sup(), toLocaleLowerCase(), toLocaleUpperCase(), toUpperCase(), trimEnd(), trimLeft(), trimRight(), trimStart() methods.

For example, the deprecated bold() method gets the primitive value of a text string object and returns a modified string tagged by the Html <b> tag.

// Define a new `String` object.
const text = new String('text');

// Returns <b>text</b> of `string`.
text.bold();

Words

Three

The method with parameters gets the primitive value and performs an action on it based on the intuitive method name with the use of its parameters and returns the changed primitive value.

For example, the replace() method gets the primitive value of the text string object and returns a modified string according to the given parameters.

// Define a new `String` object.
const text = new String('text');

// Returns 'tExt' of `string`.
text.replace('e', 'E');

// Returns 'ex' of `string`.
text.substr(1, 2);

// Returns 'texttext'
text.repeat(2);

Words

Four

The method without parameters gets part of the primitive value and returns it.

Let's define the Text object and extend it with the String to achieve this approach. The method getPrefix() returns part of the primitive value prefix, and the getText() method returns part of the primitive value text.

// Create a new `Text` object.
class Text extends String {
  constructor(public text: string, public prefix?: string) {
    super(`${prefix}${text}`);
  }
  public getPrefix(): string {
    return this.prefix;
  }
  public getText(): string {
    return this.text;
  }
}

// Define a text with prefix.
const text = new Text('my text', '<<');

// Returns Text {'<<my text', text: 'my text', prefix: '<<'}.
text;

// Returns <<.
text.getPrefix();

// Returns my text.
text.getText();

Words

Five

The method without parameters checks the existence of the part of the primitive value in the object and returns the result.

Let's define the Text object and extend it with the String to achieve this approach. The hasPrefix() method checks whether the Text object has an optional prefix, and the method hasText() seems useless because the text it's required, but not really.

// Create a new `Text` object.
class Text extends String {
  constructor(public text: string, public prefix?: string) {
    super(`${prefix ? prefix : ''}${text}`);
  }
  public hasPrefix(): boolean {
    return this.prefix !== undefined ;
  }
  public hasText(): boolean {
    return this.text !== undefined;
  }
}

// Define a text with prefix.
const text = new Text('my text', '<<');

// Returns Text {'<<my text', text: 'my text', prefix: '<<'}.
text;

// Returns true.
text.hasPrefix();

// Returns true. (noun) (noun)
text.hasText();

Words

Six

The method with parameters checks the existence of the part of the primitive value in the object and returns the result.

// Create a new `Text` object.
class Text extends String {
  constructor(public text: string, public prefix?: string) {
    super(`${prefix ? prefix : ''}${text}`);
  }
  public hasPrefix(prefix: string): boolean {
    return this.prefix !== undefined && this.prefix === prefix;
  }
  public hasText(text: string): boolean {
    return this.text !== undefined && this.text === text;
  }
}

// Define a text with prefix.
const text = new Text('my text', '<<');

// Returns Text {'<<my text', text: 'my text', prefix: '<<'}.
text;

// Returns true.
text.hasPrefix('<<');

// Returns true.
text.hasText('my text');

Words

Seven

The method without parameters checks the existence of the part of the primitive value in the part of the primitive and returns the result.

Let's define the Text object and extend it with the String to achieve this approach. The textHasPrefix() method checks whether the text includes the prefix of the primitive value. The same does hasTextPrefix() method.

// Create a new `Text` object.
class Text extends String {
  constructor(public text: string, public prefix?: string) {
    super(`${prefix}${text}`);
  }
  public textHasPrefix(): boolean {
    return typeof this.prefix === 'string' && this.text.includes(this.prefix);
  }
  public hasTextPrefix(): boolean {
    return typeof this.prefix === 'string' && this.text.includes(this.prefix);
  }
}

// Define a text with prefix.
const text = new Text('my text', '<<');

// Returns Text {'<<my text', text: 'my text', prefix: '<<'}.
text;

// Returns false.
text.textHasPrefix();

// Returns false. Method is not intuitive.
text.hasTextPrefix();

The example shows some possibilities of naming the methods and potential conflict with intuitiveness.

Words

Eight

The method with parameters gets the parts of the primitive value to perform action appropriate to the intuitive method name on given method parameter(s).

Let's define the Replacer object to explain. The replaceInText() method gets the parts of the object to perform replacing on a given text. The example does not refer directly to the String method, but a custom method showing a possibility of different usage of the replace() method.

// Define a new `String` object.
const text = new String('text');

// Create a new `Replacer` object.
class Replacer extends String {
  constructor(public searchValue: string, public replaceValue: string) {
    super(`${searchValue}=${replaceValue}`);
  }
  public replaceInText(text: string | String): string {
    return text.replace(this.searchValue, this.replaceValue);
  }
  public replaceTextIn(text: string | String): string {
    return text.replace(this.searchValue, this.replaceValue);
  }
  public inText(text: string | String): string {
    return text.replace(this.searchValue, this.replaceValue);
  }
}

// Define a replacer.
const replacer = new Replacer('e', 'E');

// Returns tExt of `string`.
replacer.replaceInText(text);

// Returns tExt of `string`.
replacer.replaceTextIn(text);

// Returns tExt of `string`.
replacer.inText(text);

The last example of the inText() method indicates the relation between the object and method name because the object's name suggests action. The same we can do with the first approach above.

// Imagine the class bold contains <b></b> and uses it to bold any text.
const bold = new bold();

// Returns <b>bolded</b> of `string`.
bold.text('bolded');

// Or to bold multiple texts.
// Returns ['<b>bolded</b>', '<b>also bolded</b>']
bold.text(['bolded', 'also bolded']);

Words

Nine

The method without parameters converts the part of the primitive value into another form, for example, object or array.

// Create a new `Text` object.
class Text extends String {
  constructor(public text: string, public prefix?: string) {
    super(`${prefix ? prefix : ''}${text}`);
  }
  public toArray(): [ string | undefined, string ] {
    return [this.prefix, this.text ];
  }
  public toObject(): { [index: string]: string } {
    return {
      0: `${this.prefix}${this.text}`
    };
  }
}

// Define a text with prefix.
const text = new Text('my text', '<<');

// Returns Text {'<<my text', text: 'my text', prefix: '<<'}.
text;

// Returns ['<<', 'my text']
text.toArray();

// Returns {0: '<<my text'}.
text.toObject();

Words

Conclusion

The main goal is to properly differentiate method names that work on the primitive value from those using the primitive value on the given method's parameters.

Misuse of words with improper order in the method name results in a different meaning and can lead to non-intuitiveness and inconsistency in the method usage and naming. To avoid such problems it is necessary to have a deeper understanding of the words consistent and intuitive.

Last updated