Skip to content
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

Custom type guard function #3262

Merged
merged 19 commits into from
Jun 9, 2015
Merged

Conversation

tinganho
Copy link
Contributor

Implements only the function part of #1007. I thought it was best to have some early feedback first before I implement the class method part. So that I know I'm on the same page.

I consider the type guard type as a new type. Pls. check my tests for all specifications right now. I'm not sure if it's according to your specs. So would be good to have feedback on that.

Another question I have is if I should ban declaring type guard types on non-function-like declarations? I haven't implemented this ban yet. Example:

var a: t is item //Error

@@ -429,6 +431,9 @@ module ts {
case SyntaxKind.TypeParameter:
bindDeclaration(<Declaration>node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes, /*isBlockScopeContainer*/ false);
break;
case SyntaxKind.TypeGuardType:
bindDeclaration(<Declaration>node, (SymbolFlags.TypeGuardType | SymbolFlags.Value), SymbolFlags.TypeGuardTypeExcludes, /*isBlockScopeContainer*/ false);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why SymbolFlags.Value?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is that even bound? it does not create a symbol.. it is not creating a new type, it is just boolean with additional properties.. correct?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the binding on a later commit.

@mhegazy
Copy link
Contributor

mhegazy commented May 26, 2015

pinging @JsonFreeman and @ahejlsberg for this one as well.

@mhegazy
Copy link
Contributor

mhegazy commented May 27, 2015

@tinganho thanks for submitting this change. I took a first stab at reviewing it. Overall i do not have major concerns, just my comments above. i would want @JsonFreeman and/or @ahejlsberg to take a look as well though.

@JsonFreeman
Copy link
Contributor

I haven't started reviewing this yet, but I don't think it's possible to only allow a certain type kind in return position because of type argument inference. You could have a function like this

function moveReturnToParameter<T>(func: () => T): (p: T) => void;

and then passing in a type guarding function would output a function that takes a type guard as a parameter.

@tinganho
Copy link
Contributor Author

function moveReturnToParameter<T>(func: () => T): (p: T) => void;

Also a function that takes a type guard type(not a type guard function) as a parameter is pretty much useless.

Let say that that the returned function is:

function(isFoo: item is Foo) {
  if(isFoo) {// This won't be able to take an argument so it is not useful?
  }
}

Maybe have the type checker catch all type guard types so that they are only assigned to function like declarations?

@tinganho tinganho force-pushed the customTypeGuard2 branch from a65d0c3 to 96a17fa Compare May 27, 2015 09:07
@JsonFreeman
Copy link
Contributor

Actually, I take that back. It is possible if you special case inference to infer boolean every time it encounters one of these type guards.

@tinganho
Copy link
Contributor Author

@JsonFreeman just for clarification do you mean that:
Passing in a type guard function to the following function

function moveReturnToParameter<T>(func: () => T): (p: T) => void;

will convert the returned function to accept a boolean instead of a type guard type?

And so every non-function declaration with a declared type guard type will be converted to boolean?

@tinganho
Copy link
Contributor Author

@mhegazy I fixed some of the issue you pointed out. Though I didn't understood some of your comments, it would be good if you could clarify some of them.


export interface TypeGuardType extends Type {
parameterName: string;
parameterIndex?: number; // Call expression argument
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the parameterName is a name found in a binding pattern, will parameterIndex be the index of the top level binding pattern? How will you then know which binding element needs to be narrowed? Also, please ensure you have tests that try to type guard a parameter inside a binding pattern.

@tinganho tinganho force-pushed the customTypeGuard2 branch from 8da7db9 to 487dff5 Compare June 6, 2015 05:59
@tinganho
Copy link
Contributor Author

tinganho commented Jun 6, 2015

I just added a restriction on type predicate signature declarations. Constructors, get/set accessors can't have type predicates. I also fixed some of the feedback.

@tinganho
Copy link
Contributor Author

tinganho commented Jun 7, 2015

Just added an error for referencing a top-level binding element. Also addressed all the other feedback.

@tinganho tinganho force-pushed the customTypeGuard2 branch from 4a14e61 to 67f9ab0 Compare June 7, 2015 10:40
@tinganho tinganho force-pushed the customTypeGuard2 branch 2 times, most recently from b34449f to 53b934c Compare June 8, 2015 03:45
@tinganho tinganho force-pushed the customTypeGuard2 branch from 53b934c to 51a43dd Compare June 8, 2015 05:34
@JsonFreeman
Copy link
Contributor

Thanks @tinganho! After these last few minor tweaks, can you merge the upstream master branch into here? Then we can work on merging this in.

@yuit
Copy link
Contributor

yuit commented Jun 8, 2015

Let me know if I miss anything here, but wonder what happens in the following example?

function isB(b: any): x is B {...}

I would assume we will give an error in this case.Also could you add the case in the tests.

@@ -8486,6 +8587,34 @@ module ts {
node.kind === SyntaxKind.FunctionExpression;
}

function getTypePredicateParameterIndex(parameterList: NodeArray<ParameterDeclaration>, parameter: Identifier): number {
let index = -1;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is index used?

@JsonFreeman
Copy link
Contributor

Yes @yuit, that code would be an error. The function getTypePredicateParameterIndex would return -1 / undefined, and then we would issue the error at https://github.com/Microsoft/TypeScript/pull/3262/files#diff-c3ed224e4daa84352f7f1abcd23e8ccaR8680

@yuit
Copy link
Contributor

yuit commented Jun 8, 2015

@JsonFreeman. I see, Thanks :)

@JsonFreeman
Copy link
Contributor

Awesome job @tinganho! Thank you for implementing this much requested feature!

JsonFreeman added a commit that referenced this pull request Jun 9, 2015
@JsonFreeman JsonFreeman merged commit 6e69a9e into microsoft:master Jun 9, 2015
@RyanCavanaugh
Copy link
Member

Really cool to see this feature go in. Great work! 🏆

@tinganho
Copy link
Contributor Author

tinganho commented Jun 9, 2015

thanks @JsonFreeman @RyanCavanaugh . 😃

@DickvdBrink
Copy link
Contributor

I like this! thanks @tinganho

@ahejlsberg
Copy link
Member

@tinganho Nice work!

@RichiCoder1
Copy link

prepares to re-write all the definition files

Nice work @tinganho!

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.