Skip to content

Instantly share code, notes, and snippets.

@silicakes
Created September 19, 2022 12:20
Show Gist options
  • Save silicakes/c3c573182887aa9c7bd8cfa064c15280 to your computer and use it in GitHub Desktop.
Save silicakes/c3c573182887aa9c7bd8cfa064c15280 to your computer and use it in GitHub Desktop.
Use a generic as a restrictive typed for mapped properties, avoiding conditional types
/*
* Conditional types can become tedious to maintian as nested ternary syntax is hard to understand
* The following suggests a different approach, using a mapped key: value pair where adding additional rules and -
* restrictions becomes much easier to read and maintain.
* [TSPlayground](https://www.typescriptlang.org/play?ssl=38&ssc=20&pln=38&pc=24#code/KYOwrgtgBAgiCWECGAbKBvAsAKCnqAIgPIDiUAvFAEQAmA9gOZUA0O+UAwjACoXUDGSAC4s2+AGIBJAMoAJPlQBm8AM4ALKjgC+OXdngghwAE6Kk-YIUYAFY3QAOKjGLwAjJMYDWALiiu6dCjASCAA3Np6Bkam5pbiqmq2Dk5YuPgqAO6IvkLGYMAR2DhRJmYWsAjIKACySPbOaXgA2nCIqAB0xCQAur4ENnaOrI1QLZUdUnK9UPHqSY6FekIAnvZxAXwAPLzAAB5GIDROnsDLdIoVbTV1AHwAFCFXvtzMUDTCSL6tVbX2TdzdACUFBuUAAbnR4DRwkVsCs1lAAEriDaUbZQPYHI5QE5nC7fVC-e7vISfS4-Or-IEg8GQ6F6fh0EAqIRQRQBXwouh8B7jFCvRTBIRgYzAYHkUHoKA6bCM5ms4xc3zI1FQO6C4QisU09AynAAen1UAAosY7MYnFkhGoDUaYMYGJBQKzzlB4ZYAORSzLZPwBIIhULSj1QVRQEB0VlIFQqeAMEBIVxBN3c+weJAQYDRKCu91QD39BjzFQe9q2qBEVwAK2A-FZKHg0VQUGQyxzIBQbZUa348EUbc8EYyICg9kGJiE8GAKleIRo+Z9EBD9Gn4cjGN2qlZBjdq09heLpZw7LodwJKE6pFe3qyEByeUsWkBekNUGs0ZU04AhMeAme+ZeJDXn4Hg+G6D7Ss+sKvgAqrGIAMG6aiWAwoAmPA-BsnQxhQCywgYVAISoMssZOEynY4IqASbOe7STLI9zoIu975E+QA)
*/
// Given a bunch of keys
enum Animal {
DOG = "dog",
CAT = "cat",
FISH = "fish"
}
// And a bunch of properties unique to each key
interface DogProps {
bark: boolean;
}
interface FishProps {
swim: true
}
// Map the keys to their respective extected data
interface AnimalMap {
[Animal.DOG]: DogProps,
[Animal.FISH]: FishProps
}
// Use the generic as the decision maker for the type to be passed
type Foo = <T extends keyof AnimalMap>(animal: T, data: AnimalMap[T]) => void;
type RFoo = <T extends keyof AnimalMap>(data: AnimalMap[T]) => void;
const foo: Foo = (animal, feature) => { }
const rFoo: RFoo = (feature) => {}
// Passes!
foo(Animal.DOG, { bark: true })
// Errors with
// Argument of type '{ swim: boolean; }' is not assignable to parameter of type 'DogProps'.
// Object literal may only specify known properties, and 'swim' does not exist in type 'DogProps'.
foo(Animal.DOG, { swim: true })
// Using the generic for static analysis only
rFoo<Animal.FISH>({ swim: true })
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment