带有映射类型和 const 断言的 TypeScript 构建器模式

TypeScript builder pattern with mapped types and const assertion

在 TypeScript 中,我可以创建一个函数 buildOptions(options, default),其中 options 是一个数组,default 是一个必须来自选项数组的值。我相信这是可以做到的,因为 mapped types and const assertions.

const buildOptions = <T, K extends T[][number]>(
  options: Readonly<T[]>,
  defaultOption: K
) => ({
  options,
  default: options.findIndex(x => x === defaultOption),
});

// works
buildOptions(['a', 'b'] as const, 'a');

// compile error: Argument of type '4' is not assignable to parameter of type '2 | 1 | 3'.
buildOptions([1, 2, 3] as const, 4);

是否可以使用构建器模式添加此级别的键入?我想要一个类似于以下内容的界面:

optionBuilder()
  .addOption(1)
  .addOption(2)
  .addOption(3)
  .setDefault(4) // compile error
  .build();

或者可能:

optionBuilder()
  .addOptions([1, 2, 3] as const)
  .setDefault(4) // compile error
  .build();

如果这不能在 TypeScript 中完成,有人知道可以用什么语言完成吗?

谢谢!

是的,我们可以:)


class Builder<T> {
    addOption<V>(v: V): Builder<T | V> {
        // do your stuff

        return this as Builder<T | V>;
    }

    setDefault(v: T): this {
        // do your stuff

        return this;
    }

    build(): T[] {        
        // return list of all options ??
    }
}

function optionBuilder(): Builder<never> {
    return new Builder();
}

optionBuilder()
    .addOption(1 as const)
    .addOption('str' as const)
    .setDefault(1)  // OK
    .setDefault(12) // compile error
    .setDefault('str') // OK
    .setDefault('strstr') // compile error

    .build();

注意

  • 所有as const都是必须的,否则1被认为是number(例如);
  • optionBuilder 的 return 类型的 never 类型是必要的,以确保我们的构建器完全没有类型,否则它将使用 unknown相反,接受一切;

希望有更好的解决办法!