【问题标题】:Should Typescript generics be used to force an interface when calling a method?调用方法时是否应该使用 Typescript 泛型来强制接口?
【发布时间】:2019-03-18 06:49:21
【问题描述】:

试图将我的头脑围绕泛型并想知道我是否在此处正确应用它。

考虑以下几点:

interface NameValuePair {
    name: string;
    value: string;
}

function flatten(data: NameValuePair[]) {
    return data.reduce((obj, pair: NameValuePair) => {
        obj[pair.name] = pair.value;
        return obj;
    }, {});
}

const formData: NameValuePair[] = [
    { name: "firstName", value: "John" },
    { name: "lastName", value: "Doe" }
];

let flattened = flatten(formData);  
// { firstName: "John", lastName: "Doe" }  

每当调用该函数时,我希望 Typescript 强制使用描述最终数据结构的自定义接口。例如,使用上面的formData,可能的界面可能如下所示:

interface ProfileForm {
    firstName: string;
    lastName: string;
}

以下是我使用泛型的尝试:

function flatten<T>(formData: NameValuePair[]): T {
    return <T>formData.reduce((obj: T, pair: NameValuePair) => {
        obj[pair.name] = pair.value;
        return obj;
    }, {});
}

let flattened = flatten<ProfileForm>(formData);
let firstName = flattened.firstName;
let age = flattened.age; // typescript error

它按预期工作,但通过测试,它似乎提供了与以下相同的结果:

function flatten(formData: NameValuePair[]) {
    return formData.reduce((obj, pair: NameValuePair) => {
        obj[pair.name] = pair.value;
        return obj;
    }, {});
}

let flattened = <ProfileForm>flatten(formData);
let firstName = flattened.firstName;
let age = flattened.age; // typescript error

在这种特殊情况下,泛型有什么好处吗?

【问题讨论】:

  • 我正在写答案,但你能说出你的真实意图吗?你想用 TypeScript 解决什么问题?为什么要拥有这样的接口?
  • 主要思想是强制任何使用 flatten() 函数的人提供一个接口来映射返回值。具体来说,flatten() 用于将表单转换为 JSON 对象。每种形式都不同,具有不同的字段。我想,既然 Typescript 是一种类型化语言,那么这种强制执行就是该语言的目的所在。我对类型强制的想法是否太过分了?
  • 好的。您是否尝试过我的答案中的解决方案?

标签: typescript typescript-generics


【解决方案1】:

这行得通

const formDataRaw: NameValuePair[] = [{ name: "fullName", value: "John Doe" }]

// please note that reduce() was unnecessary, or you used it wrong. 
// Anyway, your question should be focused, TypeScript and reduce() is 
// two unrelated things, especially in your code I didn't see how they 
// were related

const formData: NameValuePair = formDataRaw[0]

interface NameValuePair {
    name: keyof ProfileForm | keyof OtherForm;
    value: string;
}

interface ProfileForm {
    fullName: string;
}

interface OtherForm {
    otherProp: string;
}

type ResultingForm<NVP extends NameValuePair> =
    NVP['name'] extends keyof ProfileForm ? ProfileForm :
    NVP['name'] extends keyof OtherForm ? OtherForm :
    never

interface Flatten {
    <NVP extends NameValuePair>(formData: NVP): ResultingForm<NVP>
}

const flatten: Flatten = <NVP extends NameValuePair>(obj: NVP) => {
    return { [obj['name']]: obj['value'] } as unknown as ResultingForm<NVP>
}

flatten(formData) // <--ok
flatten({ name: 'fullName', value: 'zxc' }) // <--ok. Code Completion works
flatten({ name: 'otherProp', value: 'zxc' }) // <--ok. Code Completion works
flatten({ name: 'foo', value: 'zxc' }) // <-- error

【讨论】:

  • 我不确定这是否会产生我正在寻找的结果。具体来说,flatten() 应该采用对象数组,而不是单个对象。我修改了我原来的问题,希望能更清楚。
【解决方案2】:

以下是我使用泛型的尝试:

function flatten<T>(formData: NameValuePair[]): T {
  return <T>formData.reduce((obj: T, pair: NameValuePair) => {
    obj[pair.name] = pair.value;
    return obj;
  }, {});
}

好吧,您正在使用泛型,但是以您使用它的方式 没有任何好处。任何编程语言的目标都应该是使用最少的代码来提供所需的结果。在这种情况下,具有以下签名的非泛型方法在没有泛型的情况下执行相同的操作:

function flatten(formData: NameValuePair[]): ProfileForm {
  return formData.reduce((obj: T, pair: NameValuePair) => {
    obj[pair.name] = pair.value;
    return obj;
  }, {}) as ProfileForm;
}

我想人们可以使用通用方式通过相同的输入提供不同的结果,但实际上它应该只是一个接口。

所以在这种情况下不要使用泛型。那么你可能会问What exactly does Generics provide and why should I use it

【讨论】:

  • 如果ProfileForm 是唯一可能的接口,则将其用作返回类型是有意义的,但可以有任意数量的接口。例如,interface LoginForm { username: string; password: string; }interface SettingsForms { timezone: string; }。在那种情况下,让flatten()s 返回类型提示是一个空接口然后每个特定接口都扩展它是否有意义?例如interface Form {}interface ProfileForm extends Form { ... }?
  • 根据您的评论,您正在尝试将代码汇总为可以执行多项操作的内容,这有点违反了单一职责原则。您应该有一个方法来提取每种类型所需的属性。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-19
  • 2020-03-24
  • 2010-11-17
  • 2017-05-09
相关资源
最近更新 更多