如何在打字稿中使用泛型类型和联合类型

时间:2020-12-28 09:23:53

标签: typescript

我有一个类,当我初始化它时,我传入一个值,或者一个关于该值的对象

例如

new Field<string>('test')
new Field<string>({value: 'test', inactive: 'preview'})

但是 state.value 是错误的。 (“FieldState”类型上不存在属性“value”。 类型“T”上不存在属性“值”。ts(2339))

interface BoxedValue<T> {
  value: T 
  inactive: 'disabled'| 'preview'| 'hidden'
}

type FieldState<T> = BoxedValue<T> | T

class Field<T> {
  value: T
  _pendingValue: T

  constructor(state: FieldState<T>) {
    if(typeof state === 'object') {
      this.value = this._pendingValue = state.value
    } else {
      this.value = this._pendingValue = state
    }
  }
}

谢谢你的解决方案,我修改了代码,但我现在有一个新问题。

class Field<T> {
  value: T
  _pendingValue: T

  constructor(state: FieldState<T>) {
    if(this._isBoxedValue(state)) {
      this.value = this._pendingValue = state.value // Property 'value' does not exist on type 'FieldState<T>'.Property 'value' does not exist on type 'T'.
    } else {
      this.value = this._pendingValue = state
      // Type 'FieldState<T>' is not assignable to type 'T'.
      // T' could be instantiated with an arbitrary type which could be unrelated to 'FieldState<T>'.
     // Type 'BoxedValue<T>' is not assignable to type 'T'.
     // 'T' could be instantiated with an arbitrary type which could be unrelated to 'BoxedValue<T>'
    }
  }
  _isBoxedValue(state: FieldState<T>): boolean{
    return typeof state === 'object' && state !== null &&
      Object.keys(state).length === 2 && 'value' in state && 'inactive' in state;
  }
}

为什么我把判断条件放在isboxedvalue函数里的时候会出错,而在构造函数里却没有?

2 个答案:

答案 0 :(得分:0)

问题

嗨,这个问题不是每个人都会遇到的。您的 typeof state === "object" 是对 BoxedValue 的测试,但有一个小错误:T 也可以是一个对象。

解决方案 1

  • 再添加一个测试
interface BoxedValue<T> {
    value: T;
    inactive: 'disabled' | 'preview' | 'hidden';
}

type FieldState<T> = BoxedValue<T> | T;

class Field<T> {
    value: T;
    _pendingValue: T;

    constructor(state: FieldState<T>) {
        if (typeof state === 'object' && "value" in state) {
            this.value = this._pendingValue = state.value;
        } else {
            this.value = this._pendingValue = state;
        }
    }
}

解决方案 2

  • 指定 T 的类型
interface BoxedValue<T> {
    value: T;
    inactive: 'disabled' | 'preview' | 'hidden';
}

type FieldState<T> = BoxedValue<T> | T;

class Field<T extends (string | number /* or your type */)> {
    value: T;
    _pendingValue: T;

    constructor(state: FieldState<T>) {
        if (typeof state === 'object') {
            this.value = this._pendingValue = state.value;
        } else {
            this.value = this._pendingValue = state;
        }
    }
}

答案 1 :(得分:0)

您应该使用 in 运算符。使用typeof state ==='object'只能保证stateobject类型(如果state的值为null,它也是object类型),但不能保证它具有 value 属性。

<块引用>

in 运算符还充当类型的缩小表达式。

interface BoxedValue<T> {
  value: T 
  inactive: 'disabled'| 'preview'| 'hidden'
}

type FieldState<T> = BoxedValue<T> | T

class Field<T> {
  value: T
  _pendingValue: T

  constructor(state: FieldState<T>) {
    if('value' in state) {
      this.value = this._pendingValue = state.value
    } else {
      this.value = this._pendingValue = state
    }
  }
}

更新:

您应该将 User-Defined Type Guards 用于 _isBoxedValue 方法。返回类型 state is BoxedValue<T> 是一个类型谓词,而不是 boolean

例如

interface BoxedValue<T> {
  value: T 
  inactive: 'disabled'| 'preview'| 'hidden'
}

type FieldState<T> = BoxedValue<T> | T

class Field<T> {
  value: T
  _pendingValue: T

  constructor(state: FieldState<T>) {
    if(this._isBoxedValue(state)) {
      this.value = this._pendingValue = state.value;
    } else {
      this.value = this._pendingValue = state;
    }
  }
  _isBoxedValue(state: FieldState<T>): state is BoxedValue<T>{
    return typeof state === 'object' && state !== null &&
      Object.keys(state).length === 2 && 'value' in state && 'inactive' in state;
  }
}