-
-
Notifications
You must be signed in to change notification settings - Fork 110
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
Enumerate type names #32
Enumerate type names #32
Conversation
2c102b3
to
e3f729e
Compare
Slightly unrelated, but what do you think about this: https://twitter.com/felixfbecker/status/928799529410879488 ? |
type guards as return types look pretty neat, have not used them yet though. If I understand them correctly they would require |
source/index.ts
Outdated
|
||
switch (objectName) { | ||
case 'ArrayBuffer': | ||
return TypeName.ArrayBuffer; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand this switch
. If the value is "ArrayBuffer"
, you are returning "ArrayBuffer"
. The only purpose of it's seems to be to get TypeScript to accept that it's a TypeName
, but that's not very idiomatic for TypeScript. Idiomatic is to not have anything in the runtime JS for type reasons - if you only want to work around the type system, use a cast and be done with it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you point to a normative source that explains how this approach is not idiomatic / considered a bad practice?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, you can extract it from the design goals of the language: https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals
Goals
- Impose no runtime overhead on emitted programs.
- Emit clean, idiomatic, recognizable JavaScript code.
- Use a consistent, fully erasable, structural type system.
Non-Goals
- Add or rely on run-time type information in programs, or emit different code based on the results of the type system. Instead, encourage programming patterns that do not require run-time metadata.
Basically, converting a codebase from JS to TS should not change the JavaScript at all. The typings are a build-time construct that gets completely removed and should result in the same JavaScript as before.
re: type guards. It would not create a bigger API surface at all (in opposite to this PR), it just changes the return type annotation of the specific type checking functions from If you are worried about increased API surface, I would rather reconsider this enum addition and only add a union type of all the possible string values (or a |
@felixfbecker Thanks for the insights. To be clear: I meant the TypeScript surface as in "things we export in source, consumers import in source". You are right, this is true for the I have not used type guards yet, so you could clarify if this correct: I understand that to employ type guards on some of the import is, {ArrayLike} from '@sindresorhus/is';
function doThings(input: SomeThing | ArrayLike) {
if (is.arrayLike(input)) {
// input is known to be ArrayLike
} else {
// input is known to be SomeThing
}
} |
There already is an |
To be honest the jump from TypeScript design goals to your opinion is a non sequitur to me. I see how casting explicitly helps with readability, so let's do that. The enum export itself helps TypeScript consumers using Are you aware of another way to achieve the same level of type information on |
Oh yeah, I'm not arguing against adding the enum, I would consider that a feature. I was just talking about the huge // Enum whose values will get replaced at build time
export const enum TypeName { null = 'null', undefined = 'undefined' ... } or export type TypeName = 'null' | 'undefined' | ... |
We are on the same page then :) |
c6fe5fb
to
3d61e87
Compare
@sindresorhus @felixfbecker Could you have another look at this? |
// @ltetzlaff |
beautiful 👍 |
@marionebl This is not a breaking change for either TS or JS users, right? |
@sindresorhus: I think this is breaking for TypeScript consumers, e.g.: before import is from '@sindresorhus/is';
function getType(input: any): string {
return is(input);
} after import is, {TypeName} from '@sindresorhus/is';
function getType(input: any): TypeName {
return is(input);
} |
@marionebl breaking - can't use (although I might have understood incorrectly). @sindresorhus this isn't breaking. |
@gioragutt Breaking as in "consumers have to change their code when updating to new version" applies to this change, or am I missing something? |
@marionebl tbh, when you mention it, will it actually break if you mark the return type as |
As I see it this is not breaking. The enum is a subtype of |
@felixfbecker Now that this has landed, would we still benefit from adding type guards? If so, how? |
Yes, definitely! Consider this: // Without type guards
function foo(param: string | Set<string>): string {
if (is.string(param)) {
return param as string // Without the cast, this is a type error
} else {
return Array.from(param as Set<string>).join(',') // Without the cast, this is a type error
}
}
// With type guards
function foo(param: string | Set<string>): string {
if (is.string(param)) {
return param // TS now knows params is a string
} else {
return Array.from(param).join(',') // TS now knows params is a Set
}
} |
@felixfbecker @gioragutt Thanks, TIL. |
This is intended as a discussion starter outlining a possible extended TypeScript usage.
Taking
@sindresorhus/is
for a first test ride I noticed howis
could do more toprovide consumers with type information. I chose TypeScript
enum
to do so to retainAPI compatibility with JS consumers.
TypeName
TypeName
as return type foris
,getObjectType
getObjectType
andis
to map string results toTypeName
Pro
Contra