Skip to content

Latest commit

 

History

History
295 lines (210 loc) · 8.93 KB

golden-rule.md

File metadata and controls

295 lines (210 loc) · 8.93 KB

Item 51: Avoid Unnecessary Type Parameters

Things to Remember

  • Avoid adding type parameters to functions and classes that don't need them.
  • Since type parameters relate types, every type parameter must appear two or more times to establish a relationship.
  • Remember that a type parameter may appear in an inferred type.
  • Avoid "return-only generics."
  • Unneeded type parameters can often be replaced with the unknown type.

Code Samples

function identity<T>(arg: T): T {
  return arg;
}

💻 playground


const date = identity(new Date());
//    ^? const date: Date
const nums = [1, 2, 3];
//    ^? const nums: number[]
const numsCopy = nums.map(identity);
//    ^? const numsCopy: number[]

💻 playground


function identity<T>(arg: T): T {
  //           (decl.)    1   2
  return arg;
}

💻 playground


function third<A, B, C>(a: A, b: B, c: C): C {
  return c;
}

💻 playground


function third<C>(a: unknown, b: unknown, c: C): C {
  return c;
}

💻 playground


declare function parseYAML<T>(input: string): T;

💻 playground


interface Weight {
  pounds: number;
  ounces: number;
}

const w: Weight = parseYAML('');

💻 playground


declare function parseYAML<T=null>(input: string): T;
const w: Weight = parseYAML('');  // still allowed

💻 playground


declare function parseYAML(input: string): unknown;

💻 playground


const w = parseYAML('') as Weight;

💻 playground


function printProperty<T, K extends keyof T>(obj: T, key: K) {
  console.log(obj[key]);
}

💻 playground


function printProperty<T>(obj: T, key: keyof T) {
  console.log(obj[key]);
}

💻 playground


function getProperty<T, K extends keyof T>(obj: T, key: K) {
  return obj[key];
}

💻 playground


function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

💻 playground


class ClassyArray<T> {
  arr: T[];
  constructor(arr: T[]) { this.arr = arr; }

  get(): T[] { return this.arr; }
  add(item: T) { this.arr.push(item); }
  remove(item: T) {
    this.arr = this.arr.filter(el => el !== item)
  }
}

💻 playground


class Joiner<T extends string | number> {
  join(els: T[]) {
    return els.map(el => String(el)).join(',');
  }
}

💻 playground


class Joiner {
  join<T extends string | number>(els: T[]) {
    return els.map(el => String(el)).join(',');
  }
}

💻 playground


class Joiner {
  join(els: (string | number)[]) {
    return els.map(el => String(el)).join(',');
  }
}

💻 playground


function join(els: (string|number)[]) {
  return els.map(el => String(el)).join(',');
}

💻 playground


interface Lengthy {
  length: number;
}
function getLength<T extends Lengthy>(x: T) {
  return x.length;
}

💻 playground


function getLength(x: Lengthy) {
  return x.length;
}

💻 playground


function getLength(x: {length: number}) {
  return x.length;
}

💻 playground


function getLength(x: ArrayLike<unknown>) {
  return x.length;
}

💻 playground


declare function processUnrelatedTypes<A, B>(a: A, b: B): void;

💻 playground


declare function processUnrelatedTypes(a: unknown, b: unknown): void;

💻 playground


function processUnrelatedTypes<A, B>(a: A, b: B) {
    a = b;
//  ~ Type 'B' is not assignable to type 'A'.
    b = a;
//  ~ Type 'A' is not assignable to type 'B'.
}

💻 playground


function processUnrelatedTypes(a: unknown, b: unknown) {
  a = b;  // ok
  b = a;  // ok
}

💻 playground