在应用程序的边界处保持可靠的类型,那里的 DOM、网络和 JSON 都逃出了编译器的掌控。
在 Kodokon 中打开本课真实世界的代码始于完美类型终结之处:DOM、网络和 JSON 返回的数据是 TypeScript 无法猜到的。第一道边界:DOM。querySelector 返回 Element | null,这个类型太模糊了,既读不了 value 也调用不了 focus()。
const input = document.querySelector<HTMLInputElement>(
"#email"
);
if (input) {
input.value = "test@example.com";
input.focus();
}类型参数是你对编译器许下的一个承诺:定位到正确的选择器是你的责任。当元素来自某个事件时,更推荐使用 instanceof 检查,它会在运行时真正地验证类型。
const form = document.querySelector("form");
form?.addEventListener("submit", (event) => {
event.preventDefault();
if (event.target instanceof HTMLFormElement) {
console.log(new FormData(event.target));
}
});第二道边界:网络。response.json() 返回 Promise<any>,换句话说是一扇通向任何东西的敞开的门。专业的最低要求是:声明期望的类型、检查 response.ok,并把调用集中到一个带类型的函数里。
interface Post {
id: number;
title: string;
}
async function getPost(id: number): Promise<Post> {
const url = `https://api.example.com/posts/${id}`;
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return (await response.json()) as Post;
}那个 as Post 仍然只是一种信任的表态:如果 API 变了,运行时不会有任何东西提醒你。对于关键数据,请编写一个类型守卫,在使用之前验证 JSON 的实际形状。
function isPost(value: unknown): value is Post {
if (typeof value !== "object" || value === null) {
return false;
}
const obj = value as Record<string, unknown>;
return (
typeof obj.id === "number" &&
typeof obj.title === "string"
);
}