打字稿:从对象的 属性 中的 属性 确定对象类型
Typescript: determine object type from property in object's property
我可能对 Typescript 要求太多了,但我想知道这样的事情是否可行:
interface ObjectType {
type: 'this' | 'that';
}
interface SomeObject {
objType: ObjectType
}
interface ThisObject extends SomeObject {
objType: { type: 'this' }
thisProp: 'anything'
}
interface ThatObject extends SomeObject {
objType: { type: 'that' };
thatProp: 'something'
}
function getProp(obj: ThisObject | ThatObject) {
switch (obj.objType.type) {
case 'this':
return {
type: obj.objType.type,
prop: obj.thisProp
};
case 'that':
return {
type: obj.objType.type,
prop: obj.thatProp
};
}
}
Typescript 能够正确缩小 obj.objType.type
,但我试图在返回的对象中分配给 prop
的值没有进行类型检查。两者的错误相同(显然 属性 名称不同):
TS2339: Property 'thatProp' does not exist on type 'ThisObject | ThatObject'. Property 'thatProp' does not exist on type 'ThisObject'.
这样的事情可能吗?我也尝试过这样的事情:
interface SomeObject {
objType: ObjectType;
thisProp: SomeObject['objType'] extends 'this' ? 'anything' : never;
thatProp: SomeObject['objType'] extends 'that' ? 'something' : never;
}
这导致两个道具都是 never
,以及类似这样的东西:
type PickObject<T> = T extends 'this' ? ThisObject : ThatObject;
function getProp<T extends 'this' | 'that'>(obj: PickObject<T>) {
switch (obj.objType.type) {
case 'this':
return {
type: obj.objType.type,
prop: obj.thisProp
};
case 'that':
return {
type: obj.objType.type,
prop: obj.thatProp
};
}
}
这会导致相同的错误:
TS2339: Property 'thisProp' does not exist on type 'PickObject '.
似乎不需要 SomeObject
接口。这是消除了 SomeObject
接口的可能解决方案:
interface ObjectType {
type: 'this' | 'that';
}
interface ThisObject extends ObjectType {
type: 'this'
thisProp: 'anything'
}
interface ThatObject extends ObjectType {
type: 'that'
thatProp: 'something'
}
function getProp(obj: ThisObject | ThatObject) {
switch (obj.type) {
case 'this':
return {
type: obj.type,
prop: obj.thisProp
};
case 'that':
return {
type: obj.type,
prop: obj.thatProp
};
}
}
问题是 ObjectType
和 objType
键。 objType
实际上并没有定义接口的类型。这是一个工作示例:
type ObjectType = 'this' | 'that'
interface SomeObject {
type: ObjectType
}
interface ThisObject extends SomeObject {
type: 'this'
thisProp: 'anything'
}
interface ThatObject extends SomeObject {
type: 'that'
thatProp: 'something'
}
function getProp(obj: ThisObject | ThatObject) {
switch (obj.type) {
case 'this':
return {
type: obj.type,
prop: obj.thisProp
};
case 'that':
return {
type: obj.type,
prop: obj.thatProp
};
}
}
假设你的类型实际上是这样的:
interface ThisObject extends SomeObject { objType: { type: 'this' }; thisProp: 'anything' }
interface ThatObject extends SomeObject { objType: { type: 'that' }; thatProp: 'something' }
其中 type
属性实际上嵌套在 objType
属性 中,那么您 运行 进入 microsoft/TypeScript#18758 where nested discriminated unions 提交的问题并不真正受支持在打字稿中。您不能将一个受歧视的联合用作另一个受歧视的联合中的判别式;判别式必须是单例类型的联合,例如 "this" | "that"
.
无需等待 microsoft/TypeScript#18758 得到解决,您可以编写自己的 user-defined type guard function that behaves like a nested discriminated union discriminator by passing a discriminant object and using the Extract
utility type 来表示所需的缩小范围。像这样:
function nestedDiscrim<T extends object | PropertyKey, D extends object | PropertyKey>(
val: T, discriminant: D): val is Extract<T, D>;
function nestedDiscrim(val: any, discriminant: any) {
if ((typeof val === "object") && (typeof discriminant === "object")) {
for (let k in discriminant) {
if (!(k in val)) return false;
if (!nestedDiscrim(val[k], discriminant[k])) return false;
}
return true;
}
if ((typeof val !== "object") && (typeof discriminant !== "object")) {
return val === discriminant;
}
return false;
}
你会像这样使用它:
function getProp(obj: ThisObject | ThatObject) {
if (nestedDiscrim(obj, { objType: { type: "this" } } as const)) {
return {
type: obj.objType.type,
prop: obj.thisProp
};
} else {
return {
type: obj.objType.type,
prop: obj.thatProp
};
}
}
你可以看到它有效:
console.log(getProp({ objType: { type: "this" }, thisProp: "anything" })); // {type: "this", prop: "anything"};
console.log(getProp({ objType: { type: "that" }, thatProp: "something" })); // {type: "that", prop: "something"};
想法是 nestedDiscrim(obj, { objType: { type: "this" } })
走过 obj
,查看 obj.objType.type
并将其与 "this"
进行比较。如果它们相同,那么我们将 obj
从 ThisObject | ThatObject
缩小到 ThisObject
。否则,我们将 obj
从 ThisObject | ThatObject
缩小到 ThatObject
.
好的,希望对您有所帮助;祝你好运!
我可能对 Typescript 要求太多了,但我想知道这样的事情是否可行:
interface ObjectType {
type: 'this' | 'that';
}
interface SomeObject {
objType: ObjectType
}
interface ThisObject extends SomeObject {
objType: { type: 'this' }
thisProp: 'anything'
}
interface ThatObject extends SomeObject {
objType: { type: 'that' };
thatProp: 'something'
}
function getProp(obj: ThisObject | ThatObject) {
switch (obj.objType.type) {
case 'this':
return {
type: obj.objType.type,
prop: obj.thisProp
};
case 'that':
return {
type: obj.objType.type,
prop: obj.thatProp
};
}
}
Typescript 能够正确缩小 obj.objType.type
,但我试图在返回的对象中分配给 prop
的值没有进行类型检查。两者的错误相同(显然 属性 名称不同):
TS2339: Property 'thatProp' does not exist on type 'ThisObject | ThatObject'. Property 'thatProp' does not exist on type 'ThisObject'.
这样的事情可能吗?我也尝试过这样的事情:
interface SomeObject {
objType: ObjectType;
thisProp: SomeObject['objType'] extends 'this' ? 'anything' : never;
thatProp: SomeObject['objType'] extends 'that' ? 'something' : never;
}
这导致两个道具都是 never
,以及类似这样的东西:
type PickObject<T> = T extends 'this' ? ThisObject : ThatObject;
function getProp<T extends 'this' | 'that'>(obj: PickObject<T>) {
switch (obj.objType.type) {
case 'this':
return {
type: obj.objType.type,
prop: obj.thisProp
};
case 'that':
return {
type: obj.objType.type,
prop: obj.thatProp
};
}
}
这会导致相同的错误:
TS2339: Property 'thisProp' does not exist on type 'PickObject '.
似乎不需要 SomeObject
接口。这是消除了 SomeObject
接口的可能解决方案:
interface ObjectType {
type: 'this' | 'that';
}
interface ThisObject extends ObjectType {
type: 'this'
thisProp: 'anything'
}
interface ThatObject extends ObjectType {
type: 'that'
thatProp: 'something'
}
function getProp(obj: ThisObject | ThatObject) {
switch (obj.type) {
case 'this':
return {
type: obj.type,
prop: obj.thisProp
};
case 'that':
return {
type: obj.type,
prop: obj.thatProp
};
}
}
问题是 ObjectType
和 objType
键。 objType
实际上并没有定义接口的类型。这是一个工作示例:
type ObjectType = 'this' | 'that'
interface SomeObject {
type: ObjectType
}
interface ThisObject extends SomeObject {
type: 'this'
thisProp: 'anything'
}
interface ThatObject extends SomeObject {
type: 'that'
thatProp: 'something'
}
function getProp(obj: ThisObject | ThatObject) {
switch (obj.type) {
case 'this':
return {
type: obj.type,
prop: obj.thisProp
};
case 'that':
return {
type: obj.type,
prop: obj.thatProp
};
}
}
假设你的类型实际上是这样的:
interface ThisObject extends SomeObject { objType: { type: 'this' }; thisProp: 'anything' }
interface ThatObject extends SomeObject { objType: { type: 'that' }; thatProp: 'something' }
其中 type
属性实际上嵌套在 objType
属性 中,那么您 运行 进入 microsoft/TypeScript#18758 where nested discriminated unions 提交的问题并不真正受支持在打字稿中。您不能将一个受歧视的联合用作另一个受歧视的联合中的判别式;判别式必须是单例类型的联合,例如 "this" | "that"
.
无需等待 microsoft/TypeScript#18758 得到解决,您可以编写自己的 user-defined type guard function that behaves like a nested discriminated union discriminator by passing a discriminant object and using the Extract
utility type 来表示所需的缩小范围。像这样:
function nestedDiscrim<T extends object | PropertyKey, D extends object | PropertyKey>(
val: T, discriminant: D): val is Extract<T, D>;
function nestedDiscrim(val: any, discriminant: any) {
if ((typeof val === "object") && (typeof discriminant === "object")) {
for (let k in discriminant) {
if (!(k in val)) return false;
if (!nestedDiscrim(val[k], discriminant[k])) return false;
}
return true;
}
if ((typeof val !== "object") && (typeof discriminant !== "object")) {
return val === discriminant;
}
return false;
}
你会像这样使用它:
function getProp(obj: ThisObject | ThatObject) {
if (nestedDiscrim(obj, { objType: { type: "this" } } as const)) {
return {
type: obj.objType.type,
prop: obj.thisProp
};
} else {
return {
type: obj.objType.type,
prop: obj.thatProp
};
}
}
你可以看到它有效:
console.log(getProp({ objType: { type: "this" }, thisProp: "anything" })); // {type: "this", prop: "anything"};
console.log(getProp({ objType: { type: "that" }, thatProp: "something" })); // {type: "that", prop: "something"};
想法是 nestedDiscrim(obj, { objType: { type: "this" } })
走过 obj
,查看 obj.objType.type
并将其与 "this"
进行比较。如果它们相同,那么我们将 obj
从 ThisObject | ThatObject
缩小到 ThisObject
。否则,我们将 obj
从 ThisObject | ThatObject
缩小到 ThatObject
.
好的,希望对您有所帮助;祝你好运!