【问题标题】:Typescript type inference and optional object propertyTypescript 类型推断和可选对象属性
【发布时间】:2021-01-30 19:20:37
【问题描述】:

基本上我想要实现的是让 X 类从提供的构造函数参数类型推断其 K 类型。

我写了这个:

interface XContstructorParams<K extends string = never> {
    record?: Record<K, number>;
    // Other stuff with some being optionals, which is why it's all in single
    // object, as I don't want to do new X(arg1, undefined, undefined, arg2)
}

class X<K extends string = never> {
    private record: Record<K, number>;
    constructor(params: XContstructorParams<K>) {
        // I'd rather avoid to use a type assertion here but it's not a big deal
        // compared to the issue below.
        this.record = params.record || {} as Record<K, number>;
    }
    
    getNumber(k: K): number {
        return this.record[k];
    }
}

当没有给出record 属性时,这实际上可以推断 K 的类型为 never,但它不会阻止手动为 K 指定不正确的值:

// Correctly inferred to be X<'a', 'b'>
const x1 = new X({ record: {'a': 1, 'b': 2} });
// Correctly inferred to be X<never>
const x2 = new X({});
// Oops ! This is incorrect as `getNumber` will not return a number when called with 'c'
// or 'd ! But it is allowed because of how I wrote the constructor `record` property to
// be optional. I want it to be optional only when K is never. It's either you provided
// a record and it's keys determine K, or you didn't give a record and K is never, but
// should not be able to specify K and not provide a record matching your specified K.
const x3 = new X<'c' | 'd'>({});

我希望 x3 被识别为编译错误,但我无法让它工作。我做错了什么?

编辑:由于某种原因,堆栈溢出会在消息开头删除我的“你好”,所以......我猜迟到总比没有好......您好,感谢您的帮助! ;-)

【问题讨论】:

  • 为什么不class X&lt;T extends Record&lt;string, number&gt; { getNumber(k: keyof T){} }
  • 感谢您的回复。我刚刚测试了您的建议,但 const x3 = new X&lt;Record&lt;'c' | 'd', number&gt;&gt;({}); 在这种情况下也不会抛出错误。
  • 因为参数的record属性是可选的,所以当参数中省略该属性时,它只是使用与显式类型参数对应的任何类型进行实例化
  • 所以你是说我正在尝试做的事情(即得到 x3 的编译器错误)是不可能的?
  • 可以通过几种方法之一确保引发错误,但我不完全确定为什么该属性是可选的。这似乎是一个矛盾。 constructor(params: XContstructorParams&lt;K&gt; = {record: {}}) 在 XContstructorParams` 中需要 record

标签: typescript constructor optional record inference


【解决方案1】:

这个解决方案适合你吗?

interface XContstructorParams<K extends string = never> {
  record: K extends never ? undefined : Record<K, number>;
  // Other stuff with some being optionals, which is why it's all in single
  // object, as I don't want to do new X(arg1, undefined, undefined, arg2)
}

class X<K extends string = never> {
  private record: Record<K, number>;
  constructor(params: XContstructorParams<K>) {
    // I don't think you need type castin here
    this.record = params.record
  }

  getNumber(k: K): number {
    return this.record[k];
  }
}


// Correctly inferred to be X<'a', 'b'>
const x1 = new X({ record: { 'a': 1, 'b': 2 } });
const result = x1.getNumber('a') // number

const x2 = new X<never>({}); // error


const x3 = new X<'c' | 'd'>({}); // error

【讨论】:

  • 不幸的是,没有一个答案能真正满足我的需求,抱歉。正如他所说,来自 Aluan 的那个有点乱,即使Knever,你的也强制record 属性存在。在实践中,我想我也会删除record 属性的可选性。但是,只需从我的原始代码中删除 ? 就足以解决原始问题。如果我必须写record: undefined,那么我还不如明确写record: {}。还是我错过了您版本的一些其他属性?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-01-22
  • 1970-01-01
  • 1970-01-01
  • 2023-03-29
  • 2020-08-16
  • 1970-01-01
  • 2022-12-18
相关资源
最近更新 更多