Почему я получаю ошибку вывода типа в этом примере и как это сделать лучше? - PullRequest
0 голосов
/ 17 марта 2020

В следующем примере машинописи у меня есть функция getPerson, которая просто берет объект и ключ для поиска значения. Однако входной объект является Partial Generi c:

export type Person = {
    name: string;
    age: string;
}

export type People = Record<string, Person>;

function getPerson<D extends People>(partialData: Partial<D>, key: string): Person {

    if (!partialData.hasOwnProperty(key)) {
        throw new Error("key not found in dataContainer.incompleteDataSet");
    }

    return partialData[key]; // TS Lint Error here
}

Этот код, кажется, работает так, как я хочу, однако я получаю сообщение об ошибке TS Lint в операторе возврата:

TS2322: Type 'D[string] | undefined' is not assignable to type 'Person'.
  Type 'undefined' is not assignable to type 'Person

В функции getPerson Generi c D должен указывать, какой тип People мы имеем, например, D может иметь тип Parents с клавишами mom и dad.

export type Parents = {    
    mom: Person,
    dad: Person,    
}

const parentsData: Partial<Parents> = {
    mom: {
        name: "Ann",
        age: "55",
    }
}
const mom: Person = getPerson(parentsData, "mom");
console.log(mom);

Однако getPerson также должен быть открыт для работы с другими структурами Person, такими как:

const grandParentsData: Partial<Grandparents> = getGrandParentsData(); 
const grandma: Person = getPerson(grandParentsData, "grandma");

Наилучший способ, который я нашел для express, это было <D extends People> рядом с getPerson. Это может вызвать мою проблему с линтингом, поскольку D может иметь другие значения ключа, отличные от <string, Person>, как определено в People. Я прав? У вас есть идеи, как сделать это лучше?

Я ищу не лучший способ структурировать мои данные, а улучшить getPerson. (В моем реальном проекте невозможно изменить структуры данных прямо сейчас).

Вот ссылка на TypeScript Playground .

Спасибо большое!

1 Ответ

2 голосов
/ 17 марта 2020

partialData.hasOwnProperty не действует как охранник типа. (Он не может действовать как единое целое даже в принципе, поскольку key является динамическим c)

Самое простое решение - использовать ненулевое утверждение:

function getPerson<D extends People>(partialData: Partial<D>, key: string): Person {
    if (!partialData.hasOwnProperty(key)) {
        throw new Error("key not found in dataContainer.incompleteDataSet");
    }
    return  partialData[key]!;
}

Playground Link

Вы можете получить полностью типизированную версию, но ее семантика немного отличается:

function getPerson<D extends People>(partialData: Partial<D>, key: string): Person {
    const value = partialData[key]
    if (!value) {
        throw new Error("key not found in dataContainer.incompleteDataSet");
    }
    return value;
}

Playground Link

Приведенная выше версия проверяет, не является ли значение свойства неопределенным. Действительно, это, кажется, более безопасное поведение типа, так как просто потому, что объект имеет свойство, это не значит, что оно не undefined (({ age: undefined }).hasOwnProperty ("age") верно, но ({ age: undefined })['age'] не определено)

...