-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Derived types #2477
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Here's a simple way to get your Point/Centroid example working: interface Point {
x: number;
y: number;
}
interface Centroid extends Point {
_centroidBrand: any;
} Now you can assign any centroid to a point, but you will get an error if you assign a point to a centroid. For things like PhoneNumber, etc, i would do something like: interface PhoneNumber {
_phoneNumberBrand: any;
}
function createPhoneNumber(s: string) {
return <PhoneNumber><any>s;
}
function unsafeGetPhoneNumberContents(p: PhoneNumber): string {
return <string><any>p;
} Now you could pass around a phone number in your system and not ever have it be convertible to anything else implicitly. When you did want to actually get at the contents, you'd explicitly state you wanted the string form for it. You could then use that string form only for as long as necessary, and never leak that internal representation out. (Of course, you could just do this by wrapping the underlying string in a PhoneNumber class as well. but this approach allows you to avoid that wrapper, and allows you to just pass around the string as is, with no actual runtime overhead). |
That's interesting, but doesn't really work.
Or at least not without a double assertion:
Using an empty interface actually probably comes closer to what I want:
Is "tainted" the correct terminology here? If so, maybe a keyword could be used to annotate interfaces, instructing the compiler to treat it as if it were different, when doing an implicit cast, even though it isn't:
Or maybe:
Anyhow, the syntax is not terribly important to me. |
Check out #364, I think that covers basically everything you want. |
I don't know if "derived" is the correct term, but, currently, I can do something like this:
The problem is obvious - different types of IDs (all numbers) and even completely unrelated datatypes (such as timestamps) are all interchangeable; there is no type-safety for different types of data that all happen to be integers.
I would like to be able to do something along the lines of this:
The difference here, is that
UserID
andTimestamp
are actually distinct types derived from a base type (in this case a primitive), not just aliases ofnumber
.The derived type is similar to the type it was derived from - it has all of the same members.
It can be safely cast back to the type it was derived from, for example:
You cannot however automatically cast back up:
But you can of course do so with an assertion:
In other words,
UserID
is always anumber
, butnumber
is not necessarily aUserID
.Another example might be a point type:
Again, a
Centroid
is always aPoint
, but aPoint
is not aCentroid
unless you make that assertion.In short, this feature enables static type-checking of types that are technically identical - there is only one implementation, but there is more than one meaning.
Other examples might be "email", "phone number", "credit card", "IP address" and "hex color code", which are all strings, each with a particular meaning, all of which can be treated as strings, but none of which are interchangeable in any meaningful way.
The text was updated successfully, but these errors were encountered: