-
Notifications
You must be signed in to change notification settings - Fork 13k
Description
The type never
currently just mean the function return
that will never arrive or the unexpected value after type inference.
I think the type never
can be more powerful.
optional parameter: using type never
Since undefined
has two meaning: not existed
and existed but no value
, it would be better to split it out to have the clearest meaning.
-
It currently uses the
?
operator to define a optional parameter:interface IExample { a?: string; } const test1: IExample = { a: undefined, // expected error, but passed }; function funcExample(a?: string) {} funcExample(undefined); // expected error, but passed
It is expected to be error, but it is passed because it was resolved as the following code in
strictNullChecks
mode:interface IExample { a?: string | undefined } function funcExample(a?: string | undefined): void
where the
undefined
should not appear since it just can bestring
or void, so I think it should be resolved as:interface Example { a: string | never } function funcExample(a: string | never) {}
and expect its behavior as:
const example1: Example = { a: undefined; // show error message }; const example2: Example = {}; // expected passed, but error now funcExample(undefined); // show error message
-
For more about
never
with function optional parameter:function neverExample(a: never) {} neverExample(); // expected passed, but error now
Why dont I just use the following code?
function neverExample() {}
Consider the following
generic
class:abstract class Matcher<T> { public abstract rawMatch(target: T): boolean; public match(target: T): boolean { return this.rawMatch(target); } } class TestMatcher extends Matcher<never> { constructor(public bool: boolean) { super(); } public rawMatch(): boolean { return this.bool; } } const result = new TestMatcher(true).match(); // expected passed, but error now
It will be more convenient if we can use
never
with function ingeneric
class.The only way to solve this problem now is to override the method:
class TestMatcher extends Matcher<null> { constructor(public bool: boolean) { super(); } public rawMatch(): boolean { return this.bool; } public match(): boolean { return super.rawMatch(null); } }
alias for { [key: string]: T; }
{ [key: string]: T }
is a type that we use it frequently, but it looks not comfortable like number
, object
, etc.
I'd like to suggest to have an alias for it globally, for example:
type dictionary<T> = { [key: string]: T; };
function setData(data: dictionary<any>) { ... }
function setData(data: { [key: string]: any }) { ... }
function setData(data1: dictionary<any>, data2: dictionary<any>, something: any) { ... }
function setData(data1: { [key: string]: any }, data2: { [key: string]: any }, something: any) { ... }
disabling rules with inline comments
I don't know if this is duplicate ( issue 11051 ) or not, since the following code is 100% error in type-checking but 100% correct in logic.
Type inference is powerful, but sometimes it'll block some convenient usage.
Given the following class, the generic
is consider to be a state of it:
type MapCallback<Input, Output> = (value: Input) => Output;
abstract class Querier<Target, Result> {
public mapCallback: MapCallback<any, any>;
public abstract rawQuery(target: Target): Result[];
public query(target: Target): Result[] {
const results = this.rawQuery(target);
return results.map(this.mapCallback);
}
public map<MappedResult>(callback: MapCallback<Result, MappedResult>): Querier<Target, MappedResult> {
this.mapCallback = callback;
return <Querier<Target, MappedResult>>this; // error: cannot be converted
}
}
The map
method will change its state, for example:
class SomeQuerier extends Querier<object, number> { ... }
const someResults =
new SomeQuerier() // Querier<object, number>
.map((num: number) => num.toString()) // Querier<object, string>
.query(someObject); // string[]
So I think is there a way to provide some inline comments to disable rules, just like eslint
and tslint
did?
public map<MappedResult>(callback: MapCallback<Result, MappedResult>): Querier<Target, MappedResult> {
this.mapCallback = callback;
// tsc:disable-next-line type-convert-checking
return <Querier<Target, MappedResult>>this;
}
Edit: I found the following part is duplicate ( issue 10727 ), just ignore it.
Relative complement operator
We have union
operator ( |
) now, from myType1
to myType2
:
type myType1 = string | number;
type myType2 = myType1 | boolean;
// myType2 = string | number | boolean
But we can't do the opposite way from myType2
to myType1
:
type myType2 = string | number | boolean;
type myType1 = myType2 - boolean; // we cannot do this now
// expected myType1 = string | number;
If we can have the complement
operator ( -
), it will be easily to use in some Partial case (e.g. options
):
interface PartialOptions {
a?: string;
b?: number;
c?: {
c1: string;
c2: number;
}
}
If we want to get types within c
, it is impossible to do this now since c
can be undefined
:
type cnames = keyof PartialOptions['c'];
// cnames = never, since PartialOptions['c'] could be undefined
type ctypes = PartialOptions['c'][cnames]; // error: Type 'never' cannot be used as an index type
The only way to get types now is to copy it manually. but we have to copy it again while PartialOptions
changed:
type ctypes = string | number;
If we can use complement operator:
type EntireOptions = {
[T in keyof PartialOptions]: PartialOptions[T] - undefined;
}
// expected EntireExmaple = {
// a: string;
// b: number;
// c: {
// c1: string;
// c2: number;
// }
// }
type cnames = keyof EntireOptions['c'];
// expected cnames = 'c1' | 'c2'
type ctypes = EntireOptions['c'][cnames];
// expected ctypes = string | number