我正面临一个HTTP调用的响应随区域而异的情况。 我已经指定了对象的返回类型。因此,如果我假设假设有4种类型,并使用它们的联合作为包装器类型。
出现问题是因为有些领域并不是所有事物都共有的。 解决方案是使这些字段为可选。 对我来说,将字段设置为可选意味着没有必要,在这种情况下这是不正确的。这样可以使Tslint错误消失。
如果您不明白我的问题,请告诉我
编辑:-
function mapAddress(address: AddressRegionXX | AddressRegionYY,region:string): AddressWithIdXX | AddressWithIdXX {
let addressId = address.id ? address.id : "XX";
let addressType = addressId == "XX" ? "subbed" : "unsubbed";
if(region == "XX"){
return {
firstName: address.first_name || null,
lastName: address.last_name || null,
street1: address.addr_1 || null,
street2: address.addr_2 || null,
city: address.city || null,
state: address.state || null,
postalCode: address.zip_code || null,
phone: address.phone_number || null,
addressId: addressId,
addressType: addressType
};
if(region == "XX"){
return {
fName: address.f_name || null,
lName: address.l_name || null,
address: address.addr_1 || null,
roomNo: address.addr_2 || null,
district: address.district|| null,
state: address.state || null,
pinCode: address.zip_code || null,
phoneNumber: address.phone_number || null,
addressId: addressId,
addressType: addressType
};
}
}
这是我必须使用Union类型的上下文 在这里,取决于每个区域地址类型的响应将改变,这里有一个很长的列表,在这里不切实际。 正如我在此处所示,字段名称因每个区域而异,并且还有一些其他字段。 因此,解决这种情况的理想方法是使用条件类型是适当的。联合类型是否有其他选择。 与enned一样,至少会有5-6个地址类型,将来还会有更多地址类型。
In layman terms
is there any miraculous way in which :D
We write something Like
type correctTypeAddress<T> =
T extends Address? AddressXX :
T extends Address? AddressYY :
mapAddress(地址:AddressRegion,区域:字符串):correctTypeAddress
下面是我处理的所有类型不具有相同属性的示例。那么如何处理非统一类型映射
时,除了联合类型还可以使用吗?重新创建问题的方式
type typeA = {
prop1:string;
prop2:string;
}
type typeB = {
prop1: string;
prop3: string;
}
type typeC = {
prop4: string;
prop5: string;
}
type mappedType = typeA | typeB | typeC;
const a = (b): mappedType => {
return {
prop1:"1",
prop5:"3"
}
}
编辑:-应用条件类型但使用通用类型会导致另一个皮棉错误,如Property 'prop1' does not exist on type 'T'
type typeIA = {
prop1: string;
prop2: string;
}
type typeIB = {
prop1: string;
prop3: string;
}
type typeIC = {
prop4: string;
prop5: string;
}
type typeOA = {
prop1: string;
prop2: string;
}
type typeOB = {
prop1: string;
prop3: string;
}
type typeOC = {
prop4: string;
prop5: string;
}
// type mappedType = typeA | typeB | typeC;
const a = <T extends typeIA | typeIB | typeIC>(_b: T): T extends typeIA ? typeOA : never | T extends typeIB ? typeOB : never | T extends typeIC ? typeOC : never=> {
if (_b.prop1 == "1"){
return {
prop1: "1",
prop3: "3"
} as T extends typeIA ? typeOA : never | T extends typeIB ? typeOB : never | T extends typeIC ? typeOC : never
}else{
return {
prop1: "1",
prop2: "2"
} as T extends typeIA ? typeOA : never | T extends typeIB ? typeOB : never | T extends typeIC ? typeOC : never
}
}
const c = a({prop1:"1",prop2:"2"});
const d = a({ prop1: "1", prop3: "2" });
const e = a({ prop4: "1", prop5: "2" });
答案 0 :(得分:1)
我看到以下选项(您可以将它们组合在一起):
函数重载
interface AddressRegionXX {
id: string;
first_name?: string;
last_name?: string;
}
interface AddressRegionYY {
f_name?: string;
l_name?: string;
}
interface AddressWithIdXX {
firstName: string | null;
lastName: string | null;
addressId: string;
addressType: string;
}
interface AddressWithIdYY {
fName: string | null;
lName: string | null;
addressId: string;
addressType: string;
}
function mapAddress(address: AddressRegionXX, region: string): AddressWithIdXX;
function mapAddress(address: AddressRegionYY, region: string): AddressWithIdYY;
function mapAddress(address: AddressRegionXX | AddressRegionYY, region: string): AddressWithIdXX | AddressWithIdYY {
let addressId = (<AddressRegionXX>address).id ? (<AddressRegionXX>address).id : "XX";
let addressType = addressId == "XX" ? "subbed" : "unsubbed";
if (region == "XX") {
let AddressRegion = <AddressRegionXX>address;
return {
firstName: AddressRegion.first_name || null,
lastName: AddressRegion.last_name || null,
addressId: addressId,
addressType: addressType
};
}
if (region == "YY") {
let AddressRegion = <AddressRegionYY>address;
return {
fName: AddressRegion.f_name || null,
lName: AddressRegion.l_name || null,
addressId: addressId,
addressType: addressType
};
}
}
// Usage
let aXX: AddressRegionXX = { id: "idXX" };
let resXX: AddressWithIdXX = mapAddress(aXX, "XX");
let aYY: AddressRegionYY = { };
let resYY: AddressWithIdYY = mapAddress(aYY, "YY");
这里的重点是要根据结果类型获得确切类型的结果。对于类型为address
的{{1}},您将得到类型为AddressRegionXX
的结果。
此方法的问题是,在实现AddressWithIdXX
函数时应格外小心。其中的代码不了解mapAddress
的类型,因此您应该在一些附加条件下进行中继。 (Typescript代码将被转换为JavaScript,所有接口都将被删除。)
address
这里的好处是enum TypeDiscriminant {
addressXX,
addressYY
}
interface AddressRegionXX {
TypeDiscriminant: TypeDiscriminant.addressXX;
id: string;
first_name?: string;
last_name?: string;
}
interface AddressRegionYY {
TypeDiscriminant: TypeDiscriminant.addressYY;
f_name?: string;
l_name?: string;
}
interface AddressWithIdXX {
firstName: string | null;
lastName: string | null;
addressId: string;
addressType: string;
}
interface AddressWithIdYY {
fName: string | null;
lName: string | null;
addressId: string;
addressType: string;
}
function mapAddress3(address: AddressRegionXX | AddressRegionYY, region: string): AddressWithIdXX | AddressWithIdYY {
let addressId = (<AddressRegionXX>address).id ? (<AddressRegionXX>address).id : "XX";
let addressType = addressId == "XX" ? "subbed" : "unsubbed";
switch (address.TypeDiscriminant) {
case TypeDiscriminant.addressXX:
return {
firstName: address.first_name || null,
lastName: address.last_name || null,
addressId: addressId,
addressType: addressType
};
case TypeDiscriminant.addressYY:
return {
fName: address.f_name || null,
lName: address.l_name || null,
addressId: addressId,
addressType: addressType
};
}
}
// Usage
let AXX: AddressRegionXX = { id: "idXX", TypeDiscriminant: TypeDiscriminant.addressXX };
let resAXX = mapAddress3(AXX, "XX");
let AYY: AddressRegionYY = { TypeDiscriminant: TypeDiscriminant.addressYY };
let resAYY = mapAddress3(AYY, "YY");
现在知道mapAddress3
的确切类型。
问题是address
和resAXX
的类型为resAYY
。在执行AddressWithIdXX | AddressWithIdYY
之前,Typescript不知道将返回哪种类型。
第三种选项可以使用条件类型返回正确的类型
mapAddress3
预期使用量将是
function mapAddress4<T extends AddressRegionXX | AddressRegionYY>(address: T, region: string): T extends AddressRegionXX ? AddressWithIdXX : AddressWithIdYY;
但这是不可能的。参见here
答案 1 :(得分:1)
关于为什么这是“最佳”方法的完整解释(我开玩笑说,没有最佳方法)可能超出了堆栈溢出答案的范围,但从本质上讲,以下几点是简单的:
要完全获取联合类型的关键字,您需要将其转换为 (A | B)与keyof(A&B)的交集
使用“也许”(您可能在其中有两个可能的结果)加倍作为未定义/空值的解决方案,但是在这种情况下,它解决了从联合中选择时您可能会有“某物”(其类型为A或typeB或不是大小写为C的
长代码段传入。希望这有助于感觉过度,但事实并非如此。
export type UnionToIntersection<U> = [U] extends [never]
? never
: (U extends any ? (k: U) => void : never) extends ((k: infer I) => void)
? I
: never;
export type UnionMembersWith<T, K extends keyof UnionToIntersection<T>> = [T] extends [never]
? never
: Exclude<T, Exclude<T, Partial<Record<K, any>>>>;
export type Maybe<A> = _None<A> | _Some<A>;
type PickReturn<A, K extends keyof UnionToIntersection<A>> = [K] extends [never]
? typeof None
: [K] extends [keyof UnionMembersWith<A, K>]
? Maybe<NonNullable<UnionMembersWith<A, K>[K]>>
: [K] extends [keyof A]
? Maybe<NonNullable<A[K]>>
: typeof None
class _Some<A> {
readonly _tag: "some" = "some";
readonly value: A;
constructor(value: A) {
this.value = value;
}
map<U>(f: (a: A) => U): Maybe<U> {
return new _Some(f(this.value));
}
flatMap<B>(f: (a: A) => Maybe<B>): Maybe<B> {
return f(this.value);
}
pick<K extends keyof UnionToIntersection<A>>(key: K): PickReturn<A, K> {
return Maybe((this.value as any)[key]) as any;
}
get get(): A | undefined {
return this.value;
}
}
class _None<A = never> {
static value: Maybe<never> = new _None();
readonly _tag = "none";
map<U>(f: (a: A) => U): Maybe<U> {
return this as any;
}
flatMap<B>(f: (a: A) => Maybe<B>): Maybe<B> {
return this as any;
}
pick<K extends keyof UnionToIntersection<A>>(key: K): PickReturn<A, K> {
return this as any;
}
get get(): A | undefined {
return undefined;
}
getOrElse(none: never[]): [A] extends [Array<any>] ? A : A | never[];
getOrElse<B>(none: B): A | B;
getOrElse<B>(none: B): A | B {
return none as any;
}
}
export const None: Maybe<never> = _None.value;
export const Some = <A>(a: A): _Some<A> => new _Some(a);
export function Maybe<A>(value: A | null | undefined): Maybe<A> {
if (value !== null && value !== undefined) return Some(value);
return None;
}
//* END IMPLEMNTATION */
type typeIA = {
prop1: string;
prop2: string;
}
type typeIB = {
prop1: string;
prop3: string;
}
type typeIC = {
prop4: string;
prop5: string;
}
type typeOA = {
prop1: string;
prop2: string;
}
type typeOB = {
prop1: string;
prop3: string;
}
type typeOC = {
prop4: string;
prop5: string;
}
// type mappedType = typeA | typeB | typeC;
function a(_b: typeIC): typeOC
function a(_b: typeIB): typeOB
function a(_b: typeIA): typeOA
function a(_b: typeIA | typeIB | typeIC): typeOA | typeOB | typeOC {
/* 100% typesafe */
if (Maybe(_b).pick("prop1").get === "1"){
return {
prop1: "1",
prop3: "3"
}
}else{
return {
prop1: "1",
prop2: "2"
}
}
}
const c = a({prop1:"1",prop2:"2"}); // type oA
const d = a({ prop1: "1", prop3: "2" }); // type oB
const e = a({ prop4: "1", prop5: "2" }); // type oC
编辑:“了解更多”: 也许并不是要为您解决这一一次性问题,而是要解决编程中可能发生的某种“效应”。也许是monadic,而monads会捕获效果,可能是traps是非确定性的,这里的想法是Maybe可以将其抽象化,因此您不必考虑它。
这里的观点很容易被遗漏,因为其他任何溢出者都声称可以通过嵌套“ If / Else”来解决,但是Maybe的想法是它是一种抽象,您不再需要检查那里是否有东西,因此不再需要If / Else
那么多的话看起来像什么?因此,这段代码在运行时和类型级别都是安全的。
interface IPerson {
name: string;
children: IPerson[] | undefined;
}
const person = {
name: "Sally",
children: [
{
name: "Billy",
children: [
{
name: "Suzie",
children: undefined
}
]
}
]
};
const test = Maybe(person).pick("children").pick(0).pick("children").pick(0).get; // Typesafe / Runtime safe possible path
const test = Maybe(person).pick("children").pick(0).pick("children").pick(10000).get ; // Typesafe / Runtime safe impossible paths
/* We have 'Children' which is non-deterministic it could be undefined OR it could be defined */
/* Let's wrap person in Maybe so we don't care whther its there or not there anymore */
const test2 = Maybe(person).pick("children").map((childrenArr) => {
return childrenArr.map((child) => child.name.toUpperCase())
}).getOrElse([]); // string[] notice how when using things in the context of our Maybe we cease to care about undefined.
const test3 = Maybe(person).pick("children").pick(10000).map((person) => {
return {...person, name: person.name.toUpperCase()} // safe even though there's no person at Array index 10000
}).getOrElse({name: "John Doe", children: []}) // IPerson even though there is no person at Array index 10000