Every codebase past a certain size has this bug at least once: void transfer ( String fromUserId, String toAccountId, int cents) { ... } transfer (accountId, userId, 500 ); // compiles fine, wrong at runtime Both are String , so the type system has nothing to say. The usual fix is a wrapper class, which costs an allocation on every ID you touch. Extension types are the same fix without the allocation. extension type UserId ( String value) {} extension type AccountId ( String value) {} void transfer ( UserId from, AccountId to, int cents) { ... } transfer (accountId, userId, 500 ); // compile error At runtime, UserId is a String . There is no wrapper object, no field access indirection, nothing allocated. The distinction exists only in the static type system, and is erased before the program runs. What “erased” actually means This is the part that decides whether extension types fit your problem. The compiler replaces the extension type with its representation...