打字稿类型保护数组查找需要在查找之前进行过滤

Typescript type guards array find requires filtering before find

我创建了一个类型保护来检查对象是否属于特定类型。如果我尝试将数组查找与类型保护和第二个条件一起使用,我会收到类型错误,但如果我将数组查找与类型保护一起使用,然后是查找,我不会收到错误。

类型保护:

const isHtmlField = (
    field: GravityForms.Field
): field is GravityForms.HtmlField => {
    return field.type === 'html'
}

下面给我一个类型错误:

const description: GravityForms.HtmlField | undefined = fields.find(
    (item) => {
        isHtmlField(item) && item.label.toLowerCase() === "description";
    }
);

而这个有效

const description: GravityForms.HtmlField | undefined = fields
    .filter(isHtmlField)
    .find((item) => item.label.toLowerCase() === "description");

在我的 types.d.ts 中,我有以下内容:

declare namespace GravityForms {
    export interface Field {
        type: string;
        id: string;
        pageNumber: number;
        cssClass: string;
        label: string;
    }

    export interface RadioField extends Field {
        type: "radio";
        choices: {
            text: string;
            value: string;
            isSelected: boolean;
        }[];
    }

    export interface HtmlField extends Field {
        type: "html";
        content: string;
    }

    export type FormFields = (Field | RadioField | HtmlField)[];

    export interface Form {
        fields: FormFields;
        pagination: {
            pages: string[];
        };
    }
}

interface FormPage {
    title: string;
    fields: GravityForms.Form["fields"];
}

type FormPages = FormPage[];

type UpdateDataItem = (id: string, value: string) => void;

interface FormSubmissionData {
    [key: string]: string;
}

我尝试使用描述的完整页面组件:

const Page: FC<IPage> = ({ title, fields, hidden, incrementPage }) => {
    const description: GravityForms.HtmlField | undefined = fields
        .filter(isHtmlField)
        .find((item) => item.label.toLowerCase() === "description");

    console.log(`fields`, fields);

    return (
        <div
            className={classNames({
                hidden,
            })}
        >
            <div className="mb-3 md:mb-12">
                <h2 className="text-3xl leading-loose">{title}</h2>

                {description && (
                    <p className="text-white text-2xl font-bold">
                        {description.content}
                    </p>
                )}
            </div>

            {fields.map((field) => (
                <>
                    {isRadio(field) &&
                        field.cssClass.includes("convert-to-icons") && (
                            <IconRadio {...field} onClick={incrementPage} />
                        )}

                    {isTextField(field) && (
                        <>
                            <label htmlFor={field.id}>{field.label}</label>
                            <input type="text" id={field.id} name={field.id} />
                        </>
                    )}
                </>
            ))}
        </div>
    );
};

您的代码有几个问题。第一:

const description: GravityForms.HtmlField | undefined = fields.find(
    (item) => {
        isHtmlField(item) && item.label.toLowerCase() === "description";
    }
);

这里的回调函数不是谓词(函数返回boolean)。你的函数总是 returns undefined。您应该用圆括号替换大括号并删除分号以使其正常工作:

const description: GravityForms.HtmlField | undefined = fields.find(
    (item) => (
        isHtmlField(item) && item.label.toLowerCase() === "description"
    )
);

另一个问题是当你的回调函数使用类型谓词它的主体它传达给打字稿的结果类型回调函数本身。您应该 回调函数本身成为类型保护:

const descriptionBad: GravityForms.HtmlField | undefined = fields.find(
    (item): item is GravityForms.HtmlField => (
        isHtmlField(item) && item.label.toLowerCase() === "description"
    )
);

playground link

这就是为什么您使用 filter 的第二个函数按预期工作的确切原因。您正在使用 类型保护 作为那里的回调函数。