如何使用泛型方法遵循卡牌游戏规则对混合类型卡牌数组进行排序
Hey there! Let's start by addressing why your original code worked by coincidence: the default Array.sort() converts all elements to strings and compares their UTF-16 code values. In your specific case, numeric strings like "2" come before "Jack", and "Jack" < "Queen" < "King" lexicographically—this just happened to match your expected output. But this approach is fragile (for example, "10" would sort before "2" because "1" has a lower code value than "2").
To fix this properly and add generic support for type safety/reusability, here's a step-by-step solution:
1. Define a Clear Card Type (for Type Safety)
First, we'll define a type that represents valid cards—either numbers or the specific face cards. This helps catch invalid values early and enables proper generic constraints.
2. Create a Weight Mapping
Assign numeric weights to face cards so we can compare all cards consistently. Numbers use their own value as weight, while face cards follow the order Jack < Queen < King.
3. Build the Generic Sort Function
The generic function will accept an array of cards, enforce type constraints, and use a custom comparator based on our weight mapping to sort correctly.
TypeScript Implementation
// Define valid card types type Card = number | 'Jack' | 'Queen' | 'King'; // Map face cards to numeric weights for consistent comparison const cardWeights: Record<Exclude<Card, number>, number> = { Jack: 11, Queen: 12, King: 13 }; /** * Sorts an array of cards following standard card game rules * @param cards Array of valid cards to sort * @returns New sorted array (original array remains unchanged) */ function sortCards<T extends Card>(cards: T[]): T[] { // Create a copy to avoid mutating the original array return [...cards].sort((a, b) => { // Get weight for each card const weightA = typeof a === 'number' ? a : cardWeights[a]; const weightB = typeof b === 'number' ? b : cardWeights[b]; // Compare weights to sort ascending return weightA - weightB; }); } // Test the function const cards: Card[] = ['Jack', 8, 2, 6, 'King', 5, 3, 'Queen']; const sortedCards = sortCards(cards); console.log(sortedCards); // Output: [2, 3, 5, 6, 8, 'Jack', 'Queen', 'King']
JavaScript Implementation (with JSDoc Generics)
If you're using plain JavaScript, you can still add generic type hints via JSDoc for better IDE support:
/** * Sorts an array of cards following standard card game rules * @template {number | 'Jack' | 'Queen' | 'King'} T * @param {T[]} cards Array of valid cards to sort * @returns {T[]} New sorted array (original array remains unchanged) */ function sortCards(cards) { const cardWeights = { Jack: 11, Queen: 12, King: 13 }; return [...cards].sort((a, b) => { const weightA = typeof a === 'number' ? a : cardWeights[a]; const weightB = typeof b === 'number' ? b : cardWeights[b]; return weightA - weightB; }); } // Test the function const cards = ['Jack', 8, 2, 6, 'King', 5, 3, 'Queen']; console.log(sortCards(cards)); // Output: [2, 3, 5, 6, 8, 'Jack', 'Queen', 'King']
Key Benefits of This Approach
- Correctness: No longer relies on accidental string ordering—sorting is based on explicit card game rules.
- Type Safety: The generic constraint
T extends Cardensures only valid cards are passed to the function, preventing bugs from invalid values. - Immutability: We create a copy of the array before sorting, so the original array isn't modified (a best practice for predictable code).
- Extensibility: Adding new card types (like "Ace") is easy—just update the
Cardtype andcardWeightsmapping.
内容的提问来源于stack exchange,提问作者Isak

