如何使用 setter 在打字稿中初始化 readonly/private 值?

How to initialize a readonly/private value in typescript using a setter?

相关代码的一些背景知识:我正在尝试实现一个便于用户初始化的配置 class。为此,我的配置将实现配置接口的对象作为构造函数参数,而配置 class 处理默认值、验证等事情

import Uri from '../Uri'

export default interface BaseUriConfigurationInterface
{
    uri: Uri | string
    mergePath?: boolean
    mergeQuery?: boolean
    defaultScheme?: 'http' | 'https' | null
    overrideScheme?: boolean
};

在配置中,uri是唯一没有默认值的配置选项。

import ConfigurationInterface from '../contract/BaseUriConfigurationInterface'
import Uri from '../Uri'
import UriSyntaxError from '../error/UriSyntaxError'
import ConfigurationError from '../error/ConfigurationError'

export default class BaseUriConfiguration implements ConfigurationInterface
{
    private _uri: Uri
    readonly mergePath: boolean
    readonly mergeQuery: boolean
    readonly defaultScheme: 'http' | 'https' | null
    readonly overrideScheme: boolean

    constructor(config : ConfigurationInterface)
    {
        this.mergePath = true
        this.mergeQuery = false
        this.defaultScheme = null
        this.overrideScheme = false

        Object.assign(this, config);
    }

    public get uri() : Uri
    {
        return this._uri
    }

    private set uri(uri: Uri | string)
    {
        if(typeof uri === 'string') {

            try {
                uri = new Uri(uri)
            }
            catch(error) {
                if(! (error instanceof UriSyntaxError)) {
                    throw error
                }

                const msg = `Invalid base uri given. See previous error for more details.`
                throw new ConfigurationError(msg, error)
            }
        }

        this._uri = uri
    }
}

这里的想法是构造函数设置配置选项的所有默认值,而 setters 用于变异和验证数据。例如,在这种情况下,用户简单地提供一个字符串而不是初始化一个新的 uri 对象可能更方便,因此 setter 允许任一类型的参数。我正在使用 Object.assign 用用户提供的值快速覆盖任何默认值。

配置选项是只读的,因此 setter 是私有的:它只能用于初始化值一次。这是自 typescript 4.3 以来有效的,它允许 getter 和 setters.

的不同可见性

问题:

现在的问题是:Property '_uri' has no initializer and is not definitely assigned in the constructor. - 我收到这个错误是因为打字稿无法判断接口保证 uri 在构造函数中使用 Object.assign

我尝试通过在构造函数中手动添加 this.uri = config.uri 来修复此问题,但这也不起作用,因为出于某种原因我收到以下错误:Type 'string | Uri' is not assignable to type 'Uri'.

问题:

简而言之,如何使用 setter 在打字稿中初始化 readonly/private 值?或者,如果这不可能,我应该为我的配置结构使用哪种替代方法?

可能的解决方案:

我想我可以将 setter 代码直接集成到构造函数中,但由于以下原因,这种方法似乎远非理想:

  1. 如果您有很多代码跨越多个 setter,构造函数会很快变得混乱。
  2. 这不适用于 Object.assign,迫使您手动设置每个配置选项,这将非常冗长乏味。

I'm getting the error '_uri' has no initializer and is not definitely assigned in the constructor. because typescript can't tell that the interface guarantees that the uri is initialized in the constructor using Object.assign

在这种情况下,您可以通过编写

来抑制错误
private _uri!: URL
//          ^

在你的 class.

也就是说,我建议不要为此使用 setters/getters,而是编写一个方法 private static validUri(uri: Uri | string): Uri 并在构造函数中像 this.uri = BaseUriConfiguration.validUri(config.uri); 一样使用它。