You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript枚举支持正则字符串值吗?API响应类型定义求助

How to Handle Dynamic String Values in TypeScript API Response Types

Hey, I get exactly what you're trying to do here—you want to define a strict TypeScript interface for your API response, where one string field can only take specific values, including those like Substitution x where x is a positive integer. Regular enums fall short here because they require literal values, not regex patterns. Let's go through the best alternatives:

1. String Literal Union + Type Predicate (Type Guard)

This approach combines compile-time type hints with runtime validation to cover both fixed values and dynamic Substitution x cases:

First, define the fixed event types as a string literal union, then extend it to include the dynamic substitution pattern using template literals:

// Fixed, non-dynamic event types
type FixedEvent = 
  | "Normal Goal" 
  | "Own Goal" 
  | "Penalty" 
  | "Missed Penalty" 
  | "Yellow Card" 
  | "Second Yellow Card" 
  | "Red Card";

// Combine fixed types with dynamic substitution pattern
type MatchEvent = FixedEvent | `Substitution ${number}`;

Note: The Substitution ${number} template literal will technically allow zero or negative numbers in the type system. To enforce positive integers, add a runtime type guard to validate the value:

function isMatchEvent(value: string): value is MatchEvent {
  // Check if value is one of the fixed events
  const fixedEvents: FixedEvent[] = [
    "Normal Goal", "Own Goal", "Penalty", 
    "Missed Penalty", "Yellow Card", "Second Yellow Card", "Red Card"
  ];
  if (fixedEvents.includes(value as FixedEvent)) {
    return true;
  }

  // Validate substitution format with regex (positive integer only)
  return /^Substitution [1-9]\d*$/.test(value);
}

When processing your API response, use this guard to ensure type safety:

interface ApiResponse {
  event: MatchEvent;
  // Other fields...
}

// Example usage with an API response
const rawResponse = await fetch("/api/events").then(res => res.json());
if (isMatchEvent(rawResponse.event)) {
  // rawResponse.event is now typed as MatchEvent
  const validatedResponse: ApiResponse = rawResponse;
} else {
  throw new Error(`Invalid event type: ${rawResponse.event}`);
}

2. Branded Type (For Extra Strictness)

If you want to prevent accidentally assigning arbitrary strings that match the pattern but haven't been validated, use a branded type. This adds a "brand" marker to distinguish validated values:

type FixedEvent = 
  | "Normal Goal" 
  | "Own Goal" 
  | "Penalty" 
  | "Missed Penalty" 
  | "Yellow Card" 
  | "Second Yellow Card" 
  | "Red Card";

// Add a unique brand to the substitution type
type SubstitutionEvent = `Substitution ${number}` & { __brand: "ValidatedMatchEvent" };
type MatchEvent = FixedEvent | SubstitutionEvent;

function isMatchEvent(value: string): value is MatchEvent {
  const fixedEvents: FixedEvent[] = [
    "Normal Goal", "Own Goal", "Penalty", 
    "Missed Penalty", "Yellow Card", "Second Yellow Card", "Red Card"
  ];
  if (fixedEvents.includes(value as FixedEvent)) {
    return true;
  }

  if (/^Substitution [1-9]\d*$/.test(value)) {
    // Type assertion to add the brand
    return true;
  }
  return false;
}

Now, you can't assign a string like "Substitution 5" directly to a MatchEvent variable—it has to pass through the isMatchEvent guard first.

3. Runtime-Only Validation (Simpler Approach)

If you don't need strict compile-time checking and just want to ensure API responses are valid, you can use a plain string type paired with a validation function:

interface ApiResponse {
  event: string;
  // Other fields...
}

function validateApiResponse(response: unknown): asserts response is ApiResponse {
  if (typeof response !== "object" || response === null) {
    throw new Error("Invalid API response structure");
  }
  const res = response as ApiResponse;
  
  const validEventPatterns = [
    ...["Normal Goal", "Own Goal", "Penalty", "Missed Penalty", "Yellow Card", "Second Yellow Card", "Red Card"],
    /^Substitution [1-9]\d*$/
  ];

  const isValid = validEventPatterns.some(pattern => 
    typeof pattern === "string" ? pattern === res.event : pattern.test(res.event)
  );

  if (!isValid) {
    throw new Error(`Invalid event type: ${res.event}`);
  }
}

Use this validator right after fetching the response to catch invalid data early:

const rawResponse = await fetch("/api/events").then(res => res.json());
validateApiResponse(rawResponse);
// Now rawResponse is safely typed as ApiResponse

Key Takeaway

TypeScript doesn't natively support regex-constrained string types at compile time (especially for positive integers), so the sweet spot is combining string literal unions for fixed values with runtime validation via type guards. This gives you the best of both worlds: compile-time hints and runtime data integrity.

内容的提问来源于stack exchange,提问作者Philipp Kolbe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 07:43:25