-
-
Notifications
You must be signed in to change notification settings - Fork 194
Description
Bug Report
📝 Summary
- Node Version: 20.18.0
- Typia Version: 11.0.0
- Expected behavior:
typia.createValidate<schema.Recipe>()successfully compiles - Actual behavior: OOM (out of memory crash)
schema.org defines a shared vocabulary for many common web applications and is often how social media and web scraping applications derive metadata from links. The official typescript types (schema-dts) are very deep and cause OOM when used with typia.
For example, this code runs for 33min before crashing (log):
import * as typia from "typia";
import * as schema from "schema-dts";
export const validateRecipe = typia.createValidate<schema.Recipe>();I suspect that typia is missing opportunities to collapse generic trees and is actually running in an infinite loop. But I have not proven this.
⏯ Playground Link
Not applicable because this bug requires using the schema-dts npm package which is not available in the playground. Instead see the above detailed description and below code example.
💻 Code occurring the bug
import * as typia from "typia";
import * as schema from "schema-dts";
export const validateRecipe = typia.createValidate<schema.Recipe>();👾 Debugging
I believe the culprit is this schema-dts type:
type SchemaValue<T, TProperty extends string> = T | Role<T, TProperty> | readonly (T | Role<T, TProperty>)[];Because every value in a schema can also be a role (which opens up a huge subtree of types) and it's parameterized by a field, this blows up the type graph enormously. That said, typia should be able to improve execution here if it doesn't already:
interface RoleBase extends ThingBase {
/** The end date and time of the item (in {@link http://en.wikipedia.org/wiki/ISO_8601 ISO 8601 date format}). */
"endDate"?: SchemaValue<Date | DateTime, "endDate">;
/**
* A position played, performed or filled by a person or organization, as part of an organization. For example, an athlete in a SportsTeam might play in the position named 'Quarterback'.
*
* @deprecated Consider using https://schema.org/roleName instead.
*/
"namedPosition"?: SchemaValue<Text | URL, "namedPosition">;
/** A role played, performed or filled by a person or organization. For example, the team of creators for a comic book might fill the roles named 'inker', 'penciller', and 'letterer'; or an athlete in a SportsTeam might play in the position named 'Quarterback'. */
"roleName"?: SchemaValue<Text | URL, "roleName">;
/** The start date and time of the item (in {@link http://en.wikipedia.org/wiki/ISO_8601 ISO 8601 date format}). */
"startDate"?: SchemaValue<Date | DateTime, "startDate">;
}
type RoleLeaf<TContent, TProperty extends string> = RoleBase & {
"@type": "Role";
} & {
[key in TProperty]: TContent;
};
export type Role<TContent = never, TProperty extends string = never> = RoleLeaf<TContent, TProperty> | LinkRole<TContent, TProperty> | OrganizationRole<TContent, TProperty> | PerformanceRole<TContent, TProperty>;The Role type looks like the above, in which TProperty is actually only used one of the intersection types, meaning that typia should be able to use a common validation helper for RoleBase. I suspect it's not doing this and instead if fully generating the entire validation tree from Role for every field which leads to exponential complexity.
Here's an example playground using the above types.
Note that you may run into "nonsensible intersection" when working on this. See google/schema-dts#205 which has been merged but not released yet to resolve this. In the example playground above I've resolved this by adopting the same technique as seen in google/schema-dts#205.