typescript 中 interface 如何根据其中一个属性推断另一个属性

2019-10-18 15:07:20 +08:00
 johnnyNg

比如我想要 sex 属性为 male 时,tes 属性为 string,为 female 时,test 属性为 number

enum ESex {
  Male,

  Female
}

interface IPerson {
  sex: ESex;

  test: string | number;
}

const persons: IPerson[] = [
  {
    sex: ESex.Male,

    test: 1
  },

  {
    sex: ESex.Female,

    test: ''
  }
];

不能使用泛型,因为 person 总是呈现为数组形式,没法一个个添加泛型

3835 次点击
所在节点    JavaScript
16 条回复
PainAndLove
2019-10-18 16:54:52 +08:00
interface IPersonA {
sex: ESex.Male;

test: string
}
interface IPersonB {
sex: ESex.Female;

test: number
}
const persons: Array<IPersonA | IPersonB> = [
{
sex: ESex.Male,
test: 'a'
},

{
sex: ESex.Female,
test: 1
}
];
Hypn0s
2019-10-18 17:05:12 +08:00
```javascript

enum Sex {
Male,
Female
}

type Person = {
sex: Sex.Female,
test: number,
} | {
sex: Sex.Male,
test: string,
}

const persons: Array<Person> = [
{
sex: Sex.Male,
test: ""
},
{
sex: Sex.Female,
test: 1
}
];

```
PainAndLove
2019-10-18 17:12:42 +08:00
@Hypn0s 嗯,可以用联合类型包起来。
不知道有没有更好的方法
johnnyNg
2019-10-18 17:20:41 +08:00
@PainAndLove @Hypn0s 确实是个方法,但是如果属性比较多的话,写起来有点麻烦,我是想使用 extends 关键字实现的,但是 extends 关键字是匹配类型的,但是我这个好像涉及到了具体的值
luob
2019-10-18 17:26:52 +08:00
先分别定义 MalePerson 和 FemalePerson 两个接口,然后定义联合类型,type Person = MalePerson | FemalePerson
PainAndLove
2019-10-18 17:33:34 +08:00
看了下类似于 T extends K ? number : string 这样的方式。 但是这个场景套不进去。。
minsheng
2019-10-18 17:41:37 +08:00
这个可以用 conditional type 做,就是楼上说的 T extends K。你把枚举替换成 type Gender = ‘male’ | ‘female’即可。

手机打字,单引号不正确
minsheng
2019-10-18 17:43:46 +08:00
哦,看楼上的例子,枚举的字面值本身也可以做类型,那么

type Data<T> = T extends Geneder.Male ? string : (T extends Gender.Female ? number : never)

即可
PainAndLove
2019-10-18 22:33:48 +08:00
@minsheng 嗯,我也试出来了, 但是感觉这样写太不直观了。 还是分成 2 个 interface 好一点
minsheng
2019-10-19 08:43:37 +08:00
@PainAndLove 编码上确实不是很直观,但是语义上这边相当于定义了一个简单的 type-level function,我觉得更清楚,更方便代码复用。看个人喜好吧,如果只有一两处使用,interface 自然是不错的选择。
zbinlin
2019-10-19 11:05:08 +08:00
可以用 type 来定义:

enum ESex {
    Male,
    Female,
}

type IPerson = {
    sex: ESex.Male;
    test: number;
} | {
    sex: ESex.Female;
    test: string;
};

const persons: IPerson[] = [
    {
        sex: ESex.Male,
        test: 1,
    },
    {
        sex: ESex.Female,
        test: '',
    }
];
ericgui
2019-10-19 12:07:48 +08:00
老铁,您能用 Gender 吗?别用 sex
PainAndLove
2019-10-21 14:26:59 +08:00
@luob
这个需求如果放在函数中,可以通过函数的重载来实现。和这里定义 2 个 interface 是一样的性质
fishlium
2019-10-24 15:17:09 +08:00
@minsheng 这个应用场景下 type Data 怎么放到 interface Person 下呢?
minsheng
2019-10-25 11:44:19 +08:00
@fishlium 这里就需要一个 generic interface:interface Person<T extends Gender> { gender: Gender; someFieldA: FieldA<T>; someFieldB: FieldB<T> }
fishlium
2019-10-25 14:08:53 +08:00
@minsheng 谢谢,我也是这样想的,确认一下有没有更好的写法

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/610678

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX