Builder pattern using TypeScript interfaces

后端 未结 3 1027
慢半拍i
慢半拍i 2020-12-18 08:39

I would like to do something like this:

interface IPoint {
    x : number;
    y : number;
    z? : number;
}
const diag : IPoint = IPoint.x(1)
                      


        
3条回答
  •  予麋鹿
    予麋鹿 (楼主)
    2020-12-18 09:01

    The following design adds type-safety by accomplishing 3 things:

    1. It is aware which of the required properties have been already provided.
    2. It is aware which of the optional properties have been already provided.
    3. Will only let you build once you have provided all required properties.

    The Point itself:

    interface Point {
      x: number;
      y: number;
      z?: number;
    }
    
    class Point implements Point {
      constructor(point: Point) {
        Object.assign(this, point);
      }
    }
    

    The Point builder:

    class PointBuilder implements Partial {
      x?: number;
      y?: number;
      z?: number;
    
      withX(value: number): this & Pick {
        return Object.assign(this, { x: value });
      }
    
      withY(value: number): this & Pick {
        return Object.assign(this, { y: value });
      }
    
      withZ(value: number): this & Required> {
        return Object.assign(this, { z: value });
      }
    
      build(this: Point) {
        return new Point(this);
      }
    }
    

    Usage:

    /**
     * The `z` property is optional.
     */
    new PointBuilder()
      .withX(1)
      .withY(1)
      .build();
    
    /**
     * The `.build()` method cannot be called — we are still missing `y`.
     */
    new PointBuilder()
      .withX(1)
      .withZ(1);
    
    /**
     * The `z` property is correctly recognized as `number` (as opposed to `number | undefined`).
     */
    new PointBuilder()
      .withX(1)
      .withZ(1)
      .z
    

提交回复
热议问题