Следует ли использовать дженерики Typescript для принудительного вызова интерфейса при вызове метода? - PullRequest
0 голосов
/ 13 октября 2018

Пытаясь обернуть голову вокруг дженериков, и задаюсь вопросом, правильно ли я применяю их здесь.

Примите во внимание следующее:

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

В этом конкретном случае генерики дают какую-либо выгоду?

Ответы [ 2 ]

0 голосов
/ 15 октября 2018

Вот моя попытка использовать дженерики:

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;
}

Полагаю, можно использовать универсальный способ для предоставления разных результатов с одним и тем же вводом, но тогда это действительно так.должен быть просто интерфейс в этой точке.

Так что в этом случае не используйте обобщенные.Что тогда вы могли бы спросить Что конкретно предоставляет Generics и почему я должен его использовать .

0 голосов
/ 13 октября 2018

Это работает

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
...