假设我有一个界面:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<t:RequestServerVersion Version="Exchange2013_SP1" />
</soap:Header>
<soap:Body>
<m:UpdateItem MessageDisposition="SaveOnly" ConflictResolution="AlwaysOverwrite">
<m:ItemChanges>
<t:ItemChange>
<t:ItemId Id="AAMkAGVlOTZjNTM3LWVjNjgtNGZlNi04MTBkLWIyNjNjNWEyY2VlNABGAAAAAABpsgv3HB+wQJRg4K+r7AmBBwBJi9ckXu/REb74AIBfn0G8AAAUrOs1AACN8cPrPdSYR5RdhR69ULJ0AAACOkAqAAA=" ChangeKey="CQAAABYAAACN8cPrPdSYR5RdhR69ULJ0AAACR0YO" />
<t:Updates>
<t:SetItemField>
<t:FieldURI FieldURI="item:Categories" />
<t:Message>
<t:Categories>
<t:String>Muktader</t:String>
</t:Categories>
</t:Message>
</t:SetItemField>
</t:Updates>
</t:ItemChange>
</m:ItemChanges>
</m:UpdateItem>
</soap:Body>
</soap:Envelope>
然后我有一个期望该类型的子集(或完全匹配)的函数。也许它将传递整个对象,使它只会传入interface IUser {
email: string;
id: number;
phone: string;
};
。我希望类型检查器允许两者。
示例:
{email: "t@g.com"}
Typescript是否支持这种行为呢?我觉得它非常有用,特别是像Redux这样的范例。
澄清一下,目标是:
function updateUser(user: IUser) {
// Update a "subset" of user attributes:
$http.put("/users/update", user);
}
语句等命令式逻辑,这会失去编译时类型检查的好处。更新:Typescript宣布支持mapped types,一旦发布就应该解决这个问题。
答案 0 :(得分:25)
Typescript现在支持部分类型。
创建部分类型的正确方法是:
type PartialUser = Partial<IUser>;
答案 1 :(得分:2)
您可以将部分或全部字段声明为可选字段。
interface IUser {
email: string; // not optional
id?: number; // optional
phone?: string; // optional
};
答案 2 :(得分:2)
使用映射类型的正确解决方案:
updateUser<K extends keyof IUser>(userData: {[P in K]: IUser[P]}) {
...
}
答案 3 :(得分:2)
这是你想要的
type Subset<T extends U, U> = U;
这可以确保U是T的子集,并将U作为新类型返回。例如:
interface Foo {
name: string;
age: number;
}
type Bar = Subset<Foo, {
name: string;
}>;
您不能将不属于Foo的新属性添加到Bar中,也不能以不兼容的方式更改类型。这也可以递归地作用于嵌套对象。
答案 4 :(得分:1)
您可以将其分隔到不同的界面:
interface IUser {
id: number;
};
interface IUserEmail extends IUser {
email: string;
}
interface IUserPhone extends IUser {
phone: string;
}
让您的方法接收基本IUser
界面,然后检查您需要的字段:
function doit(user: IUser) {
if (user.email) {
} else if (user.phone) {
}
}
答案 5 :(得分:1)
如果我正确理解了这个问题,你需要像Flow $Shape
所以,在一个地方,你可能有一些需要类型的东西
interface IUser {
email: string;
id: number;
phone: string;
};
然后,在另一个地方你想要一个与IUser类型相同的类型,只是所有字段都是可选的。
interface IUserOptional {
email?: string;
id?: number;
phone?: string;
};
您想要一种基于IUserOptional
自动生成IUser
的方法,而无需再次写出类型。
现在,我不认为这可以在Typescript中实现。事情可能会在2.0中发生变化,但我认为我们甚至还没有接近过类似的东西。
你可以看一下预编译器,它会在打字稿之前为你生成这样的代码,但这听起来不是一件容易的事情。
考虑到这个问题,我只能建议您尝试使用Flow。在流程中,您只需执行$Shape<IUser>
即可以编程方式生成所需的类型。当然,Flow在许多大小方面都与Typescript不同,所以请记住这一点。 Flow不是编译器,所以你不会得到像Enums和类实现接口的东西
答案 6 :(得分:0)
值得注意的是,Partial<T>
如接受的答案所示,使 all 字段为可选字段,不一定是您需要的字段。
如果您想将某些字段设为必填字段(例如id
和email
),则需要将其与Pick
结合使用:
type UserWithOptionalPhone = Pick<IUser, 'id' | 'email'> & Partial<IUser>
一些解释:
Pick
的作用是让您简洁地指定接口的子集(如其他答案所建议的那样,无需创建重复字段类型的全新接口),然后让您使用它们,而< em>仅那些字段。
function hello1(user: Pick<IUser, 'id' | 'email'>) {
}
hello1({email: '@', id: 1}); //OK
hello1({email: '@'}); //Not OK, id missing
hello1({email: '@', id: 1, phone: '123'}); //Not OK, phone not allowed
现在,这不是我们真正想要的,因为我们想要允许,但不是 require 电话。为此,我们通过创建intersection type来“合并”我们类型的部分和“挑选”的版本,然后将id
和email
作为必填字段,以及所有其他内容作为可选–正是我们想要的。
function hello2(user: Pick<IUser, 'id' | 'email'> & Partial<IUser>) {
}
hello2({email: '@', id: 1}); //OK
hello2({email: '@', id: 1, phone: '123'}); //OK
hello2({email: '@'}); //Not OK, id missing