To fix #5930 and #6474:
let anything: any;
if (isFoo(anything)) {
declare anything: Foo;
anything.foo // valid and type-checkable
}
interface Foo {
foo: number;
}
function isFoo(something: any): something is Foo {
return typeof something.foo === "number";
}
let node: Node
if (node.nodeType === 1) {
declare node: Element;
node.addEventListener // valid
}
// Added on August 2, 2016
// Dog|House case from #9999
function fn(x: any) {
if (x instanceof Animal) {
declare x: Dog; // User knows better than the type system, so let it go explicitly
x.woof(); // type-checked
} else {
declare x: House;
// handle House case
}
}
// Added on August 8, 2016
try {
foo();
}
catch (err) {
// when I'm absolutely sure that my error object will always be an instance of Error
declare err: Error;
console.error(err.messge) // compiler warns
}
{
declare anything: Foo;
// `anything` is Foo in this block.
// if type of `anything` is not narrow-able into Foo, it should be an error.
}
any type cannot be narrowed by type guards. (#5930)Declare a new variable. var element = node as Element
+1 for this feature. I was also thinking of another way to have "type assertion type" assertion work in if statement (oops, just found that it is the same as what issue #6474 mentioned a long time ago, and referenced by SaschaNaz at the first place):
interface Yo {
type: 'yo';
}
let foo: any;
if ((foo.type === 'yo') as foo is Yo) {
// Now `foo` has type `Yo`.
}
the example with Node to Element is exactly what type guards were designed for
function isElement(node: Node): node is Element { return node.nodeType === 1; }
what is wrong with defining a super-small function for it?
i think that using declare node: Element; undermines:
node.nodeType === 1 and declared node: Element not bound together and can easily go out of sync during refactoringtype-safety:
node.nodeType === 1anddeclared node: Elementnot bound together and can easily go out of sync during refactoring
I think the unboundedness can help sometimes with its flexibility. What about this:
// jQuery ajax
$.ajax({
url: "http://example.com/foo.xml",
dataType: "xml"
}).then(data => {
// data is any
declare data: XMLDocument; // I know better than the type system!
});
PS: Specifically, I posted this suggestion because I had to catch an error and get error type.
try {
/* ... */
}
catch (err) {
if (isWebIDLParseError(err)) {
const werr = err as WebIDL2.WebIDLParseError; // type narrowing does not work :(
console.warn(`A syntax error has found in a WebIDL code line ${werr.line} from ${result.url}:\n${werr.input}\n`);
}
else {
throw new Error(`An error occured while converting WebIDL from ${result.url}: ${err.message || err}`);;
}
}
first: narrowing from any is one of my favorite dreams to dream of, so i am with you on that
the ajax problem isn't a problem as long as you develop a contract between input and output:
interface Request<Data> {
url: string;
dataType: string;
}
function fetchData<Data>(request: Request<Data>) : Promise<Data> {
return $ajax(request);
}
const request: Request<XMLDocument> = {
url: "http://example.com/foo.xml",
dataType: "xml"
};
fetchData(request).then(xml => { /* xml is XMLDocument */ })
I posted this suggestion because I had to catch an error and get error type.
@SaschaNaz see also #8677
narrowing from
anyis one of my favorite dreams to dream of
@aleksey-bykov amen, same here. The compiler treats any annotations as meaning _"I don't_ want _any type checking on this variable"_. But what I often wish to convey to the compiler is _"this variable could be anything at this point. I can't make assumptions until I narrow it"_, for which any also _seems_ like the clearest and most readable annotation, but isn't right. I think {} works best in this case but I find it has unclear intent when I later re-read the code.
For me the best of both world would be that any still means _"don't type-check this"_, but if I explicitly put it through a type guard then I'm saying _"please do narrow this now since I'm explicitly asking you to"_. This is also more consistent with runtime semantics.
After all, an explicit type cast _does_ work on any, so why not an explicit type guard?
But it's not going to happen because it's too much of a breaking change to make people change the annotation of x in the following example from any to Dog|House, which would make it work perfectly.
// By contract, this function accepts only a Dog or a House
function fn(x: any) {
// For whatever reason, user is checking against a base class rather
// than the most-specific class. Happens a lot with e.g. HTMLElement
if (x instanceof Animal) {
x.woof(); // Disallowed if x: Animal
} else {
// handle House case
}
}
If TypeScript accepted a breaking change here with the advice to use a union type annotation rather than any in examples like this, I not sure if there would be any other objection to narrowing from any.
we want type guards to allow you to do more, not less.
...then let them work with any
anycan already do everything
...except be narrowed even when explicitly requested
@yortus i've ranted a bit right past that example you mentioned, i have a strong opinion about the shady business that is going on there
@aleksey-bykov your rant is spot on. Shady business indeed.
related #9999
( @rozzzly deleted post by mistake, original post: https://github.com/Microsoft/TypeScript/issues/9946#issuecomment-236096893 )
Great, and I think this can be better for boundedness sake.
function foo(x: any) {
// type of x is `any`
if (someCondition): x is number[] { // doesn't matter what this is
// now type of x is `number[]`
x.push (7);
}
// type of x is `any`
}
_I accidentally deleted my post prior to saschanaz's because the buttons on my phone are so small. :rage: This is what it said:_
I've always wanted something like
function foo(x: any) {
// type of x is `any`
if (someCondition) { // doesn't matter what this is
// type of x `any`
x is number[];
// type of x is `number[]`
x.push(7);
}
// type of x is `any`
}
I think this would be a good fit! It's simple and looks/behaves like a type guard. It seems like a natural extension of the concept. Unfortunatly, is is not a reserved keyword but I doubt many people name their variables "is", but throw it behind a flag if you're worried about breaking changes. I would imagine however that the lexer could be made to differentiate a type assertion of this style from other code.
@SaschaNaz ooh that's nice too, definitely helps by defining the scope within which that type has been narrowed. But, I think adding it to an if could be somewhat restrictive because it has to be accompanied by an if. I can't think of any practical examples where that would be an issue off the top of my head though :/
oh and what about this...
function foo(x: string | number) {
// type of x is `string | number`
if (someCondition): x is string {
console.log(x.toUpperCase()); // valid because we know x is a string
} else {
// typeof x is `string | number`
// someone could get confused here and think that x is a
// `number` like you can do with type guards on unions
// but the condition very well might depend on something
// other that the type of x being a string for example
if (true === false && _.isString(x)): x is string {
// will never occur because true !== false
} else {
// x could still be a string.
}
}
}
oh and what about this...
That problem also occurs with type guard functions.
function isString(something: any): something is string {
return true === false && _.isString(x)); // ???
}
Let's track this at #10421
Most helpful comment
+1 for this feature. I was also thinking of another way to have "type assertion type" assertion work in
ifstatement (oops, just found that it is the same as what issue #6474 mentioned a long time ago, and referenced by SaschaNaz at the first place):