【问题标题】:TypeScript second parameter type based on first parameter type基于第一个参数类型的 TypeScript 第二个参数类型
【发布时间】:2017-11-03 09:39:34
【问题描述】:

我有第一个参数的可能值的枚举,我希望第二个参数基于第一个参数。因此,如果给出 NAME,我希望第二个参数是字符串。如果给定 AGE,我希望第二个参数是一个数字。

我该怎么做?

enum KeyType {
   NAME,
   AGE
}

class MyClass {
   public static setProperty(key: KeyType.NAME, value: string): void { }
   public static setProperty(key: KeyType.AGE, value: number): void { } 
}

我想这样调用方法:
MyClass.setProperty(KeyType.NAME, 'John');

此外,这应该显示一个错误:
MyClass.setProperty(KeyType.NAME, 5); // 5 不是字符串

在这个例子中它不起作用,因为key类型被错误定义(键类型实际上是枚举的值,所以键类型是0)。

我也愿意接受有关具有仅允许特定参数键的特定类型的功能的不同方法的建议。

【问题讨论】:

  • 您的意思是希望它根据您碰巧放入MyClass 的内容计算出类型?那些可能有不止一种类型的东西呢?您不只是使用属性name: string;age: number; 是否有特殊原因?
  • @jonrsharpe 我希望它是一个重载的方法。所以,同样的方法,但是第二个参数的类型是基于第一个参数的 KeyType 值。所以,你建议像MyClass.name = string 这样的东西,但我想要像MyClass.set('name', string) 这样的东西。这样,当名称存储在变量中时,我可以动态设置属性值。另外,这样我就有了一个enum 可以更改的所有可用属性。
  • 你不需要keyof 的枚举。您可以使用thing[name] 动态分配。但我认为这类似于stackoverflow.com/questions/46909089/…
  • @jonrsharpe 我会调查一下,我不知道 keyof 运算符。
  • 嗯,这似乎接近我想要的:typescriptlang.org/docs/handbook/release-notes/…

标签: javascript typescript types enums overloading


【解决方案1】:

您想使用函数重载来使类型检查正常工作:

enum KeyType {
   NAME,
   AGE
}

class MyClass {
   public static setProperty(key: KeyType.NAME, value: string): void;
   public static setProperty(key: KeyType.AGE, value: number): void;
   public static setProperty(key: KeyType, value: (string | number)): void {}
}

或更简单,只需使用字符串:

class MyClass {
   public static setProperty(key: 'name', value: string): void;
   public static setProperty(key: 'age', value: number): void;
   public static setProperty(key: string, value: (string | number)): void {}
}

MyClass.setProperty('name', 42); // Argument of type '"name"' is not assignable to parameter of type '"age"'.
MyClass.setProperty('age', 42); // Ok
MyClass.setProperty('name', 'foo'); // Ok
MyClass.setProperty('age', 'foo'); // Argument of type '"foo"' is not assignable to parameter of type 'number'.

当然,您不必在重载中列出文字字符串,您可以将类似的字符串分组到类型定义中:

type StringProperty = 'name' | 'address';
type NumberProperty = 'age' | 'salary';
class MyClass {
   public static setProperty(key: StringProperty, value: string): void;
   public static setProperty(key: NumberProperty, value: number): void;
   public static setProperty(key: string, value: (string | number)): void {}
}

【讨论】:

  • 酷,第一个版本真的有效吗?所以我只需要添加另一个包含所有其他功能的(或通用的)功能吗?我通常不喜欢使用字符串,因为它更难执行,也更难重构。
  • 谢谢,它成功了,我使用了第一个版本(带有枚举),并结合了相同类型的分组属性的最新提示。
【解决方案2】:

你可以试试这个

MyClass.setProperty(key: KeyType, value: string |number) {
  // check key type and throw error as appropriate 
}

【讨论】:

  • 这是个好主意,唯一的问题是它将在运行时检查类型,而不是在编译时。所以如果我尝试使用不匹配的参数调用函数,我不会得到任何 TS 错误。
  • 它应该在您的代码编辑器中显示错误。键必须是 KeyType 枚举,值必须是字符串或数字
  • 是的,但我希望如果 KeyType 为 NAME,则该值只能是字符串,而不是数字。在您的情况下,如果我执行MyClass.setProperty(KeyType.NAME, 5);,编辑器不会显示错误,但它应该,因为名称只能是一个字符串。
  • @IsraelOnomeAgwa 我相信 OP 希望类型由分配给该属性的第一件事来确定,而不是分配给所有属性的所有类型的事物的联合。
  • 感谢您的编辑。我能想到的唯一选择是将函数设置为 MyClass.setProperty(keyname|keytype, name?|string, age?|number)
【解决方案3】:

我正在使用另一种方法,使用对象。

例如:假设我们正在创建一个函数来发送分析数据,所以我们有一个事件名称和一个关于此事件的有效负载...

enum TEvents {
  productView = 'web/product/view',
  productPutInCart = 'web/product/put-in-cart'
}

type TLogEvent = ({ event, data }:
  {
    event: TEvents.productView,
    payload: {
      productId: number
    }
  } |
  {
    event: TEvents.productPutInCart,
    payload: {
      productId: number,
      amount: number
    }
  }
) => void

const logEvent: TLogEvent = ({ event, data }) => {
  ...

【讨论】:

  • 这是一种有趣的方法,但它更多的是一种变通方法,而不是问题的实际答案。我认为您的方法允许更复杂的声明,但代价是必须编写更多代码并更改实际生成的 JavaScript 并创建更多对象,仅用于编译时类型检查。
【解决方案4】:

javascript只包含对象,所以如果你想使用枚举,只需定义它和对象然后调用它:

var enum1 = { SMALL:"asjdh", MEDIUM:2};
console.log(enum1.SMALL)
console.log(enum1.MEDIUM)

【讨论】:

    【解决方案5】:

    这个方法怎么样 - 如果你的类有两个属性,比如年龄和名字,你的 setProperty 函数可以获取字符串或年龄,并根据类型分配给特定的属性,例如:

    class MyClass {
        public age:number;
        public name:string;
    
        setProperty(value: string|number) {
            if (typeof value === 'string') {
                this.name = value;
            } else if (typeof value === 'number') {
                this.age = value;
            }
        }
    }
    
    let myClass = new MyClass();
    
    myClass.setProperty("John");
    console.log(myClass.name);
    myClass.setProperty(60);
    console.log(myClass.age);
    

    当然,使用此技巧与两种类型(字符串或数字)的值,您可以随心所欲地完成它:

    setProperty(key: string, value: string|number) {
        if (key === 'AGE') {
            this.age = value;
        } else if (key === 'NAME') {
            this.name = value;
        }
    }
    

    但是在这种情况下你需要编写更多的代码来保护这部分。

    【讨论】:

      猜你喜欢
      • 2023-02-13
      • 1970-01-01
      • 1970-01-01
      • 2011-03-05
      • 2019-11-25
      • 1970-01-01
      • 2018-03-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多