-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Generic type aliases #3397
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
Generic type aliases #3397
Changes from all commits
9c6e6ac
eb8a0e8
bcdbc98
b82ae85
33517c4
7c2a3c2
c96eee0
cd59573
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -338,6 +338,7 @@ module ts { | |
case SyntaxKind.ArrowFunction: | ||
case SyntaxKind.ModuleDeclaration: | ||
case SyntaxKind.SourceFile: | ||
case SyntaxKind.TypeAliasDeclaration: | ||
return ContainerFlags.IsContainerWithLocals; | ||
|
||
case SyntaxKind.CatchClause: | ||
|
@@ -385,10 +386,10 @@ module ts { | |
|
||
function declareSymbolAndAddToSymbolTableWorker(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags): Symbol { | ||
switch (container.kind) { | ||
// Modules, source files, and classes need specialized handling for how their | ||
// Modules, source files, and classes need specialized handling for how their | ||
// members are declared (for example, a member of a class will go into a specific | ||
// symbol table depending on if it is static or not). As such, we defer to | ||
// specialized handlers to take care of declaring these child members. | ||
// symbol table depending on if it is static or not). We defer to specialized | ||
// handlers to take care of declaring these child members. | ||
case SyntaxKind.ModuleDeclaration: | ||
return declareModuleMember(node, symbolFlags, symbolExcludes); | ||
|
||
|
@@ -406,9 +407,10 @@ module ts { | |
case SyntaxKind.ObjectLiteralExpression: | ||
case SyntaxKind.InterfaceDeclaration: | ||
// Interface/Object-types always have their children added to the 'members' of | ||
// their container. They are only accessible through an instance of their | ||
// container, and are never in scope otherwise (even inside the body of the | ||
// object / type / interface declaring them). | ||
// their container. They are only accessible through an instance of their | ||
// container, and are never in scope otherwise (even inside the body of the | ||
// object / type / interface declaring them). An exception is type parameters, | ||
// which are in scope without qualification (similar to 'locals'). | ||
return declareSymbol(container.symbol.members, container.symbol, node, symbolFlags, symbolExcludes); | ||
|
||
case SyntaxKind.FunctionType: | ||
|
@@ -424,11 +426,12 @@ module ts { | |
case SyntaxKind.FunctionDeclaration: | ||
case SyntaxKind.FunctionExpression: | ||
case SyntaxKind.ArrowFunction: | ||
case SyntaxKind.TypeAliasDeclaration: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why does this get its type parameters added to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Type parameters of classes and interfaces are in the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we move the class and interface type parameters to the locals as well (and create a locals if we don't have one already)? Seems really strange that they are stored in the members, and certainly not consistent with type aliases. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, I'd like to leave it this way. We wouldn't gain anything from the move, in fact it would just introduce another symbol table that needs to be visited. Also, the spec actually calls for type parameters and members to be in the same declaration space (even though it isn't observable right now because all members are values, but it would be if we ever support local types in classes and/or interfaces). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I always thought of locals as entities that could be accessed without qualification (they are in scope), whereas members require qualification (they are not in scope). This is why it seemed natural that type parameters would be locals, while class elements would be members. If that is not the difference between locals and members, what is the difference? |
||
// All the children of these container types are never visible through another | ||
// symbol (i.e. through another symbol's 'exports' or 'members'). Instead, | ||
// they're only accessed 'lexically' (i.e. from code that exists underneath | ||
// symbol (i.e. through another symbol's 'exports' or 'members'). Instead, | ||
// they're only accessed 'lexically' (i.e. from code that exists underneath | ||
// their container in the tree. To accomplish this, we simply add their declared | ||
// symbol to the 'locals' of the container. These symbols can then be found as | ||
// symbol to the 'locals' of the container. These symbols can then be found as | ||
// the type checker walks up the containers, checking them for matching names. | ||
return declareSymbol(container.locals, undefined, node, symbolFlags, symbolExcludes); | ||
} | ||
|
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -946,6 +946,7 @@ module ts { | |
|
||
export interface TypeAliasDeclaration extends Declaration, Statement { | ||
name: Identifier; | ||
typeParameters?: NodeArray<TypeParameterDeclaration>; | ||
type: TypeNode; | ||
} | ||
|
||
|
@@ -1539,7 +1540,9 @@ module ts { | |
export interface SymbolLinks { | ||
target?: Symbol; // Resolved (non-alias) target of an alias | ||
type?: Type; // Type of value symbol | ||
declaredType?: Type; // Type of class, interface, enum, or type parameter | ||
declaredType?: Type; // Type of class, interface, enum, type alias, or type parameter | ||
typeParameters?: TypeParameter[]; // Type parameters of type alias (undefined if non-generic) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i would call this typeAliasTypeParameter. That way it's clear from use that it's only intended for Type aliases. "typeParameters" on its own is very vague, and makes it unclear at the use site that it's only for these cases, and not for anything else with type parameters. (same with instantiations). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we either keep it this way or switch to a family of xxxSymbolLinks types like we have for types. But my preference would be to just keep it this way to reduce the noise. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would not be opposed to starting a small family of xxxSymbolLinks types. I actually think that would be a rather nice way of compartmentalizing things. |
||
instantiations?: Map<Type>; // Instantiations of generic type alias (undefined if non-generic) | ||
mapper?: TypeMapper; // Type mapper for instantiation alias | ||
referenced?: boolean; // True if alias symbol has been referenced as a value | ||
unionType?: UnionType; // Containing union type for union property | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
//// [genericTypeAliases.ts] | ||
type Tree<T> = T | { left: Tree<T>, right: Tree<T> }; | ||
|
||
var tree: Tree<number> = { | ||
left: { | ||
left: 0, | ||
right: { | ||
left: 1, | ||
right: 2 | ||
}, | ||
}, | ||
right: 3 | ||
}; | ||
|
||
type Lazy<T> = T | (() => T); | ||
|
||
var ls: Lazy<string>; | ||
ls = "eager"; | ||
ls = () => "lazy"; | ||
|
||
type Foo<T> = T | { x: Foo<T> }; | ||
type Bar<U> = U | { x: Bar<U> }; | ||
|
||
// Deeply instantiated generics | ||
var x: Foo<string>; | ||
var y: Bar<string>; | ||
x = y; | ||
y = x; | ||
|
||
x = "string"; | ||
x = { x: "hello" }; | ||
x = { x: { x: "world" } }; | ||
|
||
var z: Foo<number>; | ||
z = 42; | ||
z = { x: 42 }; | ||
z = { x: { x: 42 } }; | ||
|
||
type Strange<T> = string; // Type parameter not used | ||
var s: Strange<number>; | ||
s = "hello"; | ||
|
||
interface Tuple<A, B> { | ||
a: A; | ||
b: B; | ||
} | ||
|
||
type Pair<T> = Tuple<T, T>; | ||
|
||
interface TaggedPair<T> extends Pair<T> { | ||
tag: string; | ||
} | ||
|
||
var p: TaggedPair<number>; | ||
p.a = 1; | ||
p.b = 2; | ||
p.tag = "test"; | ||
|
||
function f<A>() { | ||
type Foo<T> = T | { x: Foo<T> }; | ||
var x: Foo<A[]>; | ||
return x; | ||
} | ||
|
||
function g<B>() { | ||
type Bar<U> = U | { x: Bar<U> }; | ||
var x: Bar<B[]>; | ||
return x; | ||
} | ||
|
||
// Deeply instantiated generics | ||
var a = f<string>(); | ||
var b = g<string>(); | ||
a = b; | ||
|
||
|
||
//// [genericTypeAliases.js] | ||
var tree = { | ||
left: { | ||
left: 0, | ||
right: { | ||
left: 1, | ||
right: 2 | ||
} | ||
}, | ||
right: 3 | ||
}; | ||
var ls; | ||
ls = "eager"; | ||
ls = function () { return "lazy"; }; | ||
// Deeply instantiated generics | ||
var x; | ||
var y; | ||
x = y; | ||
y = x; | ||
x = "string"; | ||
x = { x: "hello" }; | ||
x = { x: { x: "world" } }; | ||
var z; | ||
z = 42; | ||
z = { x: 42 }; | ||
z = { x: { x: 42 } }; | ||
var s; | ||
s = "hello"; | ||
var p; | ||
p.a = 1; | ||
p.b = 2; | ||
p.tag = "test"; | ||
function f() { | ||
var x; | ||
return x; | ||
} | ||
function g() { | ||
var x; | ||
return x; | ||
} | ||
// Deeply instantiated generics | ||
var a = f(); | ||
var b = g(); | ||
a = b; |
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.
Why is this a container with locals, but classes and interfaces are not?