泛型类型函数中的“某物”类型不存在属性

时间:2021-02-15 03:52:13

标签: typescript generic-type-argument

为什么它不起作用,当我检查 phone 是否存在时。 TypeScript 应该捕捉到并知道 phone 是否存在,那么它必须是 AddressWithPhone 接口。

这么久了,我可能没有正确定义类型,

interface Address {
    street: string;
    zip: string;
    state: string;
    city: string;
}

interface AddressWithPhone extends Address {
    phone: string;
}


interface EditAddressProps<T = Address | AddressWithPhone> {
    address: T;
    onChange: (address: T) => unknown;
}

const something = <T,>(props: EditAddressProps<T>): string => {
    // Error: Property 'phone' does not exist on type 'T'
    if (props.address.phone) {
        return 'Its AddressWithPhone';
    }
    return 'Its Address without phone';
}

TS 游乐场:

https://www.typescriptlang.org/play?#code/JYOwLgpgTgZghgYwgAgIIBN1QgZx8gbwChlTkcxsIwAucy0AcwG4SyAvYABzoqidZl6cSLwYgWbUgmBgAnmP4TWAXyJFQkWIhQYsuHAHVZACwAKJgPYgUEAB6QQ6fHux5CU5Fys3FAomrqmtDwSMgAouiyrgZmUJZcOAA8ACrIALxomG74AD5Z+njGYOY+EAB8HkJw2QZ0KYJk1gDCJnASEHQAFDWFOPUAlBmVAK4gANYglgDuIKrqCNYU5JYAttQmTBnIqQA05V1c8Yl0kdG1eHEJySnlA34Sw1VkwDDIh8c4AHS9OV-e1ggQ2IQiE2DAIygIGQAHIAJJgFwXIymCyAmGNUhqMHUSHQ+GIgo5ZDTUyWEZgLxlDEBIhAA

2 个答案:

答案 0 :(得分:0)

当您执行 <T = Address | AddressWithPhone> 时,您将 T 的默认值设置为联合 Address | AddressWithPhone。您实际上并未限制 T 的可能值,因此 T 仍然可以是任何值。

您想使用 extends 来限制 T 的可能值。这仍然不能与联合完美配合,因为 phone 未在 Address 上定义,并且必须在联合的所有成员上定义属性才能访问它们。我们想要一个具有 Address 必需和 phone 可选的所有属性的基本类型。有很多方法可以定义它,但这里有一个:

type BaseAddress = Address & Partial<AddressWithPhone>
const something = <T extends BaseAddress>(props: EditAddressProps<T>): string => {

Playground Link

答案 1 :(得分:0)

这是另一种方法:

type AddressBase = {
    street: string;
    zip: string;
    state: string;
    city: string;
};

type Address =
    | AddressBase
    | AddressBase & {
        phone: string;
    }

type EditAddressProps<T> = {
    address: T;
    onChange: (address: T) => unknown;
}

const withPhone = (props: EditAddressProps<any>): props is EditAddressProps<AddressBase & {
        phone: string;
    }> => Object.prototype.hasOwnProperty.call(props.address, 'phone');

const withoutPhone = (props: EditAddressProps<any>): props is EditAddressProps<AddressBase> =>
    !withPhone(props)


const something = < T extends Address>(props: EditAddressProps<T>) => {
    if (withPhone(props)) {
        const phone = props.address.phone; // string
        props.onChange({
            street: '123',
            zip: 'AB12345',
            state: 'TX',
            city: 'Dallas',
            phone: 'sdf', // phone is required here
        })
        return 'hello'
    }

    if (withoutPhone(props)) {
        const x = props.address // no phone
        props.onChange({
            street: '123',
            zip: 'AB12345',
            state: 'TX',
            city: 'Dallas',
            phone: 'sdf' // expected error, phone property is redundant here
        })
    }

    const defaultCase = props; // EditAddressProps<T>


    return 'default'

}

Playground link

如果您要处理带有泛型的联合类型,我建议您使用类型保护,因为 TS 无法像您期望的那样缩小范围