I wrote it again by summarizing the contents of the article.
I want to know how to apply the interface according to the object property value.
I wrote a simple example code below.
There are a total of three nodes: root, dept, and user.
The type corresponding to each node is specified. Therefore, we want the interface to be designated according to the object type property.
However, you can see that you are manually affirming the type because you have not found a solution.
In order to solve this problem, I think it should be solved with an index type rather than a conditional type.
The reason is that the conditional type is expected to be calculated and returned according to the generic type parameter, but the current problem should be calculated based on the existing type rather than receiving the type parameter.
However, the problem could not be solved even with two methods. Any help would be appreciated 🙇♂️
export enum NodeType {
root = "root",
dept = "dept",
user = "user",
}
interface Node {
readonly type: NodeType;
title: string;
}
interface RootNode extends Node {
type: NodeType.root;
}
interface DeptNode extends Node {
type: NodeType.dept;
departmentCode: string;
}
interface UserNode extends Node {
type: NodeType.user;
employeeNumber: string;
}
const nodeData: Node[] = [
{
type: NodeType.root,
title: "Company 1",
} as RootNode,
{
type: NodeType.dept,
title: "Department 1",
departmentCode: "557",
// The type is not inferred according to the object property type, so the type must be explicitly asserted.
} as DeptNode,
{
type: NodeType.user,
title: "User 1",
employeeNumber: "201911",
} as UserNode,
];
// I want to be the type of DeptNode interface because the type of object property is NodeType.dept.
const selectDept = nodeData.filter((x) => x.type === NodeType.dept);
selectDept.forEach((x) => {
console.log({
type: x.type,
title: x.title,
// The type is not affirmed in the node that fits the object property type, so you have to affirm the type yourself.
departmentCode: (x as DeptNode).departmentCode,
});
});
I know how to do it by using type
s, instead of interface
s.
Your attempt has two problems:
Node
type as the union of the three possible cases).filter
and forEach
, the inferral
algorithm isn't smart enough to understand what is the requested
type, hence you have to use a simple for
and a if
. Read more here: https://stackoverflow.com/a/62033938/632445Here we go:
export enum NodeType {
root = "root",
dept = "dept",
user = "user",
}
type NodeBase = {
readonly type: NodeType;
title: string;
}
type RootNode = NodeBase & {
type: NodeType.root;
}
type DeptNode = NodeBase & {
type: NodeType.dept;
departmentCode: string;
}
type UserNode = NodeBase & {
type: NodeType.user;
employeeNumber: string;
}
type Node = RootNode | DeptNode | UserNode;
const nodeData: Node[] = [
{
type: NodeType.root,
title: "Company 1",
},
{
type: NodeType.dept,
title: "Department 1",
departmentCode: "557",
// The type is not inferred according to the object property type, so the type must be explicitly asserted.
},
{
type: NodeType.user,
title: "User 1",
employeeNumber: "201911",
},
];
// I want to be the type of DeptNode interface because the type of object property is NodeType.dept.
for (const x of nodeData) {
if (x.type === NodeType.dept) {
console.log({
type: x.type,
title: x.title,
// The type is not affirmed in the node that fits the object property type, so you have to affirm the type yourself.
departmentCode: (x as DeptNode).departmentCode,
});
}
}