mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-12-15 08:20:53 -06:00
Explicitly encode keyof behaviors for never and unknown into getIndexType (#30753)
* Explicitly encode keyof behaviors for never and unknown into getIndexType * Merge similar cases
This commit is contained in:
parent
3dc78b6f3b
commit
d405662eb6
@ -9841,7 +9841,8 @@ namespace ts {
|
||||
maybeTypeOfKind(type, TypeFlags.InstantiableNonPrimitive) ? getIndexTypeForGenericType(<InstantiableType | UnionOrIntersectionType>type, stringsOnly) :
|
||||
getObjectFlags(type) & ObjectFlags.Mapped ? filterType(getConstraintTypeFromMappedType(<MappedType>type), t => !(noIndexSignatures && t.flags & (TypeFlags.Any | TypeFlags.String | TypeFlags.Number))) :
|
||||
type === wildcardType ? wildcardType :
|
||||
type.flags & TypeFlags.Any ? keyofConstraintType :
|
||||
type.flags & TypeFlags.Unknown ? neverType :
|
||||
type.flags & (TypeFlags.Any | TypeFlags.Never) ? keyofConstraintType :
|
||||
stringsOnly ? !noIndexSignatures && getIndexInfoOfType(type, IndexKind.String) ? stringType : getLiteralTypeFromProperties(type, TypeFlags.StringLiteral) :
|
||||
!noIndexSignatures && getIndexInfoOfType(type, IndexKind.String) ? getUnionType([stringType, numberType, getLiteralTypeFromProperties(type, TypeFlags.UniqueESSymbol)]) :
|
||||
!noIndexSignatures && getNonEnumNumberIndexInfo(type) ? getUnionType([numberType, getLiteralTypeFromProperties(type, TypeFlags.StringLiteral | TypeFlags.UniqueESSymbol)]) :
|
||||
|
||||
@ -0,0 +1,85 @@
|
||||
tests/cases/compiler/complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts(33,5): error TS2322: Type '{ type: T; localChannelId: string; }' is not assignable to type 'NewChannel<ChannelOfType<T, TextChannel> | ChannelOfType<T, EmailChannel>>'.
|
||||
Type '{ type: T; localChannelId: string; }' is not assignable to type 'Pick<ChannelOfType<T, TextChannel> | ChannelOfType<T, EmailChannel>, "type">'.
|
||||
Types of property 'type' are incompatible.
|
||||
Type 'T' is not assignable to type 'ChannelOfType<T, TextChannel>["type"] & ChannelOfType<T, EmailChannel>["type"]'.
|
||||
Type '"text" | "email"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"] & ChannelOfType<T, EmailChannel>["type"]'.
|
||||
Type '"text"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"] & ChannelOfType<T, EmailChannel>["type"]'.
|
||||
Type '"text"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
|
||||
Type 'T' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
|
||||
Type '"text" | "email"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
|
||||
Type '"text"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
|
||||
Type '"text"' is not assignable to type 'T & "text"'.
|
||||
Type '"text"' is not assignable to type 'T'.
|
||||
Type 'T' is not assignable to type 'T & "text"'.
|
||||
Type '"text" | "email"' is not assignable to type 'T & "text"'.
|
||||
Type '"text"' is not assignable to type 'T & "text"'.
|
||||
Type '"text"' is not assignable to type 'T'.
|
||||
Type 'T' is not assignable to type '"text"'.
|
||||
Type '"text" | "email"' is not assignable to type '"text"'.
|
||||
Type '"email"' is not assignable to type '"text"'.
|
||||
|
||||
|
||||
==== tests/cases/compiler/complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts (1 errors) ====
|
||||
interface TextChannel {
|
||||
id: string;
|
||||
type: 'text';
|
||||
phoneNumber: string;
|
||||
}
|
||||
|
||||
interface EmailChannel {
|
||||
id: string;
|
||||
type: 'email';
|
||||
addres: string;
|
||||
}
|
||||
|
||||
type Channel = TextChannel | EmailChannel;
|
||||
|
||||
export type ChannelType = Channel extends { type: infer R } ? R : never;
|
||||
|
||||
type Omit<T, K extends keyof T> = Pick<
|
||||
T,
|
||||
({ [P in keyof T]: P } & { [P in K]: never } & { [x: string]: never })[keyof T]
|
||||
>;
|
||||
|
||||
type ChannelOfType<T extends ChannelType, A = Channel> = A extends { type: T }
|
||||
? A
|
||||
: never;
|
||||
|
||||
|
||||
export type NewChannel<T extends Channel> = Pick<T, 'type'> &
|
||||
Partial<Omit<T, 'type' | 'id'>> & { localChannelId: string };
|
||||
|
||||
|
||||
export function makeNewChannel<T extends ChannelType>(type: T): NewChannel<ChannelOfType<T>> {
|
||||
const localChannelId = `blahblahblah`;
|
||||
return { type, localChannelId };
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
!!! error TS2322: Type '{ type: T; localChannelId: string; }' is not assignable to type 'NewChannel<ChannelOfType<T, TextChannel> | ChannelOfType<T, EmailChannel>>'.
|
||||
!!! error TS2322: Type '{ type: T; localChannelId: string; }' is not assignable to type 'Pick<ChannelOfType<T, TextChannel> | ChannelOfType<T, EmailChannel>, "type">'.
|
||||
!!! error TS2322: Types of property 'type' are incompatible.
|
||||
!!! error TS2322: Type 'T' is not assignable to type 'ChannelOfType<T, TextChannel>["type"] & ChannelOfType<T, EmailChannel>["type"]'.
|
||||
!!! error TS2322: Type '"text" | "email"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"] & ChannelOfType<T, EmailChannel>["type"]'.
|
||||
!!! error TS2322: Type '"text"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"] & ChannelOfType<T, EmailChannel>["type"]'.
|
||||
!!! error TS2322: Type '"text"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
|
||||
!!! error TS2322: Type 'T' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
|
||||
!!! error TS2322: Type '"text" | "email"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
|
||||
!!! error TS2322: Type '"text"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
|
||||
!!! error TS2322: Type '"text"' is not assignable to type 'T & "text"'.
|
||||
!!! error TS2322: Type '"text"' is not assignable to type 'T'.
|
||||
!!! error TS2322: Type 'T' is not assignable to type 'T & "text"'.
|
||||
!!! error TS2322: Type '"text" | "email"' is not assignable to type 'T & "text"'.
|
||||
!!! error TS2322: Type '"text"' is not assignable to type 'T & "text"'.
|
||||
!!! error TS2322: Type '"text"' is not assignable to type 'T'.
|
||||
!!! error TS2322: Type 'T' is not assignable to type '"text"'.
|
||||
!!! error TS2322: Type '"text" | "email"' is not assignable to type '"text"'.
|
||||
!!! error TS2322: Type '"email"' is not assignable to type '"text"'.
|
||||
}
|
||||
|
||||
const newTextChannel = makeNewChannel('text');
|
||||
// This should work
|
||||
newTextChannel.phoneNumber = '613-555-1234';
|
||||
|
||||
const newTextChannel2 : NewChannel<TextChannel> = makeNewChannel('text');
|
||||
// Compare with this, which ofc works.
|
||||
newTextChannel2.phoneNumber = '613-555-1234';
|
||||
|
||||
@ -0,0 +1,59 @@
|
||||
//// [complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts]
|
||||
interface TextChannel {
|
||||
id: string;
|
||||
type: 'text';
|
||||
phoneNumber: string;
|
||||
}
|
||||
|
||||
interface EmailChannel {
|
||||
id: string;
|
||||
type: 'email';
|
||||
addres: string;
|
||||
}
|
||||
|
||||
type Channel = TextChannel | EmailChannel;
|
||||
|
||||
export type ChannelType = Channel extends { type: infer R } ? R : never;
|
||||
|
||||
type Omit<T, K extends keyof T> = Pick<
|
||||
T,
|
||||
({ [P in keyof T]: P } & { [P in K]: never } & { [x: string]: never })[keyof T]
|
||||
>;
|
||||
|
||||
type ChannelOfType<T extends ChannelType, A = Channel> = A extends { type: T }
|
||||
? A
|
||||
: never;
|
||||
|
||||
|
||||
export type NewChannel<T extends Channel> = Pick<T, 'type'> &
|
||||
Partial<Omit<T, 'type' | 'id'>> & { localChannelId: string };
|
||||
|
||||
|
||||
export function makeNewChannel<T extends ChannelType>(type: T): NewChannel<ChannelOfType<T>> {
|
||||
const localChannelId = `blahblahblah`;
|
||||
return { type, localChannelId };
|
||||
}
|
||||
|
||||
const newTextChannel = makeNewChannel('text');
|
||||
// This should work
|
||||
newTextChannel.phoneNumber = '613-555-1234';
|
||||
|
||||
const newTextChannel2 : NewChannel<TextChannel> = makeNewChannel('text');
|
||||
// Compare with this, which ofc works.
|
||||
newTextChannel2.phoneNumber = '613-555-1234';
|
||||
|
||||
|
||||
//// [complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.js]
|
||||
"use strict";
|
||||
exports.__esModule = true;
|
||||
function makeNewChannel(type) {
|
||||
var localChannelId = "blahblahblah";
|
||||
return { type: type, localChannelId: localChannelId };
|
||||
}
|
||||
exports.makeNewChannel = makeNewChannel;
|
||||
var newTextChannel = makeNewChannel('text');
|
||||
// This should work
|
||||
newTextChannel.phoneNumber = '613-555-1234';
|
||||
var newTextChannel2 = makeNewChannel('text');
|
||||
// Compare with this, which ofc works.
|
||||
newTextChannel2.phoneNumber = '613-555-1234';
|
||||
@ -0,0 +1,130 @@
|
||||
=== tests/cases/compiler/complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts ===
|
||||
interface TextChannel {
|
||||
>TextChannel : Symbol(TextChannel, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 0, 0))
|
||||
|
||||
id: string;
|
||||
>id : Symbol(TextChannel.id, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 0, 23))
|
||||
|
||||
type: 'text';
|
||||
>type : Symbol(TextChannel.type, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 1, 15))
|
||||
|
||||
phoneNumber: string;
|
||||
>phoneNumber : Symbol(TextChannel.phoneNumber, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 2, 17))
|
||||
}
|
||||
|
||||
interface EmailChannel {
|
||||
>EmailChannel : Symbol(EmailChannel, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 4, 1))
|
||||
|
||||
id: string;
|
||||
>id : Symbol(EmailChannel.id, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 6, 24))
|
||||
|
||||
type: 'email';
|
||||
>type : Symbol(EmailChannel.type, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 7, 15))
|
||||
|
||||
addres: string;
|
||||
>addres : Symbol(EmailChannel.addres, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 8, 18))
|
||||
}
|
||||
|
||||
type Channel = TextChannel | EmailChannel;
|
||||
>Channel : Symbol(Channel, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 10, 1))
|
||||
>TextChannel : Symbol(TextChannel, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 0, 0))
|
||||
>EmailChannel : Symbol(EmailChannel, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 4, 1))
|
||||
|
||||
export type ChannelType = Channel extends { type: infer R } ? R : never;
|
||||
>ChannelType : Symbol(ChannelType, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 12, 42))
|
||||
>Channel : Symbol(Channel, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 10, 1))
|
||||
>type : Symbol(type, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 14, 43))
|
||||
>R : Symbol(R, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 14, 55))
|
||||
>R : Symbol(R, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 14, 55))
|
||||
|
||||
type Omit<T, K extends keyof T> = Pick<
|
||||
>Omit : Symbol(Omit, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 14, 72))
|
||||
>T : Symbol(T, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 16, 10))
|
||||
>K : Symbol(K, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 16, 12))
|
||||
>T : Symbol(T, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 16, 10))
|
||||
>Pick : Symbol(Pick, Decl(lib.es5.d.ts, --, --))
|
||||
|
||||
T,
|
||||
>T : Symbol(T, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 16, 10))
|
||||
|
||||
({ [P in keyof T]: P } & { [P in K]: never } & { [x: string]: never })[keyof T]
|
||||
>P : Symbol(P, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 18, 8))
|
||||
>T : Symbol(T, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 16, 10))
|
||||
>P : Symbol(P, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 18, 8))
|
||||
>P : Symbol(P, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 18, 32))
|
||||
>K : Symbol(K, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 16, 12))
|
||||
>x : Symbol(x, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 18, 54))
|
||||
>T : Symbol(T, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 16, 10))
|
||||
|
||||
>;
|
||||
|
||||
type ChannelOfType<T extends ChannelType, A = Channel> = A extends { type: T }
|
||||
>ChannelOfType : Symbol(ChannelOfType, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 19, 2))
|
||||
>T : Symbol(T, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 21, 19))
|
||||
>ChannelType : Symbol(ChannelType, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 12, 42))
|
||||
>A : Symbol(A, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 21, 41))
|
||||
>Channel : Symbol(Channel, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 10, 1))
|
||||
>A : Symbol(A, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 21, 41))
|
||||
>type : Symbol(type, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 21, 68))
|
||||
>T : Symbol(T, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 21, 19))
|
||||
|
||||
? A
|
||||
>A : Symbol(A, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 21, 41))
|
||||
|
||||
: never;
|
||||
|
||||
|
||||
export type NewChannel<T extends Channel> = Pick<T, 'type'> &
|
||||
>NewChannel : Symbol(NewChannel, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 23, 12))
|
||||
>T : Symbol(T, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 26, 23))
|
||||
>Channel : Symbol(Channel, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 10, 1))
|
||||
>Pick : Symbol(Pick, Decl(lib.es5.d.ts, --, --))
|
||||
>T : Symbol(T, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 26, 23))
|
||||
|
||||
Partial<Omit<T, 'type' | 'id'>> & { localChannelId: string };
|
||||
>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --))
|
||||
>Omit : Symbol(Omit, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 14, 72))
|
||||
>T : Symbol(T, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 26, 23))
|
||||
>localChannelId : Symbol(localChannelId, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 27, 39))
|
||||
|
||||
|
||||
export function makeNewChannel<T extends ChannelType>(type: T): NewChannel<ChannelOfType<T>> {
|
||||
>makeNewChannel : Symbol(makeNewChannel, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 27, 65))
|
||||
>T : Symbol(T, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 30, 31))
|
||||
>ChannelType : Symbol(ChannelType, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 12, 42))
|
||||
>type : Symbol(type, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 30, 54))
|
||||
>T : Symbol(T, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 30, 31))
|
||||
>NewChannel : Symbol(NewChannel, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 23, 12))
|
||||
>ChannelOfType : Symbol(ChannelOfType, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 19, 2))
|
||||
>T : Symbol(T, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 30, 31))
|
||||
|
||||
const localChannelId = `blahblahblah`;
|
||||
>localChannelId : Symbol(localChannelId, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 31, 9))
|
||||
|
||||
return { type, localChannelId };
|
||||
>type : Symbol(type, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 32, 12))
|
||||
>localChannelId : Symbol(localChannelId, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 32, 18))
|
||||
}
|
||||
|
||||
const newTextChannel = makeNewChannel('text');
|
||||
>newTextChannel : Symbol(newTextChannel, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 35, 5))
|
||||
>makeNewChannel : Symbol(makeNewChannel, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 27, 65))
|
||||
|
||||
// This should work
|
||||
newTextChannel.phoneNumber = '613-555-1234';
|
||||
>newTextChannel.phoneNumber : Symbol(phoneNumber, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 2, 17))
|
||||
>newTextChannel : Symbol(newTextChannel, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 35, 5))
|
||||
>phoneNumber : Symbol(phoneNumber, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 2, 17))
|
||||
|
||||
const newTextChannel2 : NewChannel<TextChannel> = makeNewChannel('text');
|
||||
>newTextChannel2 : Symbol(newTextChannel2, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 39, 5))
|
||||
>NewChannel : Symbol(NewChannel, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 23, 12))
|
||||
>TextChannel : Symbol(TextChannel, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 0, 0))
|
||||
>makeNewChannel : Symbol(makeNewChannel, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 27, 65))
|
||||
|
||||
// Compare with this, which ofc works.
|
||||
newTextChannel2.phoneNumber = '613-555-1234';
|
||||
>newTextChannel2.phoneNumber : Symbol(phoneNumber, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 2, 17))
|
||||
>newTextChannel2 : Symbol(newTextChannel2, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 39, 5))
|
||||
>phoneNumber : Symbol(phoneNumber, Decl(complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts, 2, 17))
|
||||
|
||||
@ -0,0 +1,96 @@
|
||||
=== tests/cases/compiler/complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts ===
|
||||
interface TextChannel {
|
||||
id: string;
|
||||
>id : string
|
||||
|
||||
type: 'text';
|
||||
>type : "text"
|
||||
|
||||
phoneNumber: string;
|
||||
>phoneNumber : string
|
||||
}
|
||||
|
||||
interface EmailChannel {
|
||||
id: string;
|
||||
>id : string
|
||||
|
||||
type: 'email';
|
||||
>type : "email"
|
||||
|
||||
addres: string;
|
||||
>addres : string
|
||||
}
|
||||
|
||||
type Channel = TextChannel | EmailChannel;
|
||||
>Channel : Channel
|
||||
|
||||
export type ChannelType = Channel extends { type: infer R } ? R : never;
|
||||
>ChannelType : "text" | "email"
|
||||
>type : R
|
||||
|
||||
type Omit<T, K extends keyof T> = Pick<
|
||||
>Omit : Pick<T, ({ [P in keyof T]: P; } & { [P in K]: never; } & { [x: string]: never; })[keyof T]>
|
||||
|
||||
T,
|
||||
({ [P in keyof T]: P } & { [P in K]: never } & { [x: string]: never })[keyof T]
|
||||
>x : string
|
||||
|
||||
>;
|
||||
|
||||
type ChannelOfType<T extends ChannelType, A = Channel> = A extends { type: T }
|
||||
>ChannelOfType : ChannelOfType<T, A>
|
||||
>type : T
|
||||
|
||||
? A
|
||||
: never;
|
||||
|
||||
|
||||
export type NewChannel<T extends Channel> = Pick<T, 'type'> &
|
||||
>NewChannel : NewChannel<T>
|
||||
|
||||
Partial<Omit<T, 'type' | 'id'>> & { localChannelId: string };
|
||||
>localChannelId : string
|
||||
|
||||
|
||||
export function makeNewChannel<T extends ChannelType>(type: T): NewChannel<ChannelOfType<T>> {
|
||||
>makeNewChannel : <T extends "text" | "email">(type: T) => NewChannel<ChannelOfType<T, TextChannel> | ChannelOfType<T, EmailChannel>>
|
||||
>type : T
|
||||
|
||||
const localChannelId = `blahblahblah`;
|
||||
>localChannelId : "blahblahblah"
|
||||
>`blahblahblah` : "blahblahblah"
|
||||
|
||||
return { type, localChannelId };
|
||||
>{ type, localChannelId } : { type: T; localChannelId: string; }
|
||||
>type : T
|
||||
>localChannelId : string
|
||||
}
|
||||
|
||||
const newTextChannel = makeNewChannel('text');
|
||||
>newTextChannel : NewChannel<TextChannel>
|
||||
>makeNewChannel('text') : NewChannel<TextChannel>
|
||||
>makeNewChannel : <T extends "text" | "email">(type: T) => NewChannel<ChannelOfType<T, TextChannel> | ChannelOfType<T, EmailChannel>>
|
||||
>'text' : "text"
|
||||
|
||||
// This should work
|
||||
newTextChannel.phoneNumber = '613-555-1234';
|
||||
>newTextChannel.phoneNumber = '613-555-1234' : "613-555-1234"
|
||||
>newTextChannel.phoneNumber : string
|
||||
>newTextChannel : NewChannel<TextChannel>
|
||||
>phoneNumber : string
|
||||
>'613-555-1234' : "613-555-1234"
|
||||
|
||||
const newTextChannel2 : NewChannel<TextChannel> = makeNewChannel('text');
|
||||
>newTextChannel2 : NewChannel<TextChannel>
|
||||
>makeNewChannel('text') : NewChannel<TextChannel>
|
||||
>makeNewChannel : <T extends "text" | "email">(type: T) => NewChannel<ChannelOfType<T, TextChannel> | ChannelOfType<T, EmailChannel>>
|
||||
>'text' : "text"
|
||||
|
||||
// Compare with this, which ofc works.
|
||||
newTextChannel2.phoneNumber = '613-555-1234';
|
||||
>newTextChannel2.phoneNumber = '613-555-1234' : "613-555-1234"
|
||||
>newTextChannel2.phoneNumber : string
|
||||
>newTextChannel2 : NewChannel<TextChannel>
|
||||
>phoneNumber : string
|
||||
>'613-555-1234' : "613-555-1234"
|
||||
|
||||
@ -1,12 +1,10 @@
|
||||
tests/cases/compiler/infiniteConstraints.ts(4,37): error TS2536: Type '"val"' cannot be used to index type 'B[Exclude<keyof B, K>]'.
|
||||
tests/cases/compiler/infiniteConstraints.ts(21,21): error TS2536: Type '"val"' cannot be used to index type 'Extract<T[K], Record<"val", string>>'.
|
||||
tests/cases/compiler/infiniteConstraints.ts(21,57): error TS2536: Type '"val"' cannot be used to index type 'Extract<T[Exclude<keyof T, K>], Record<"val", string>>'.
|
||||
tests/cases/compiler/infiniteConstraints.ts(31,43): error TS2322: Type 'Record<"val", "dup">' is not assignable to type 'never'.
|
||||
tests/cases/compiler/infiniteConstraints.ts(31,63): error TS2322: Type 'Record<"val", "dup">' is not assignable to type 'never'.
|
||||
tests/cases/compiler/infiniteConstraints.ts(36,71): error TS2536: Type '"foo"' cannot be used to index type 'T[keyof T]'.
|
||||
|
||||
|
||||
==== tests/cases/compiler/infiniteConstraints.ts (6 errors) ====
|
||||
==== tests/cases/compiler/infiniteConstraints.ts (4 errors) ====
|
||||
// Both of the following types trigger the recursion limiter in getImmediateBaseConstraint
|
||||
|
||||
type T1<B extends { [K in keyof B]: Extract<B[Exclude<keyof B, K>], { val: string }>["val"] }> = B;
|
||||
@ -30,10 +28,6 @@ tests/cases/compiler/infiniteConstraints.ts(36,71): error TS2536: Type '"foo"' c
|
||||
declare function ensureNoDuplicates<
|
||||
T extends {
|
||||
[K in keyof T]: Extract<T[K], Value>["val"] extends Extract<T[Exclude<keyof T, K>], Value>["val"]
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
!!! error TS2536: Type '"val"' cannot be used to index type 'Extract<T[K], Record<"val", string>>'.
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
!!! error TS2536: Type '"val"' cannot be used to index type 'Extract<T[Exclude<keyof T, K>], Record<"val", string>>'.
|
||||
? never
|
||||
: any
|
||||
}
|
||||
|
||||
@ -31,7 +31,8 @@ type K03 = keyof boolean; // "valueOf"
|
||||
type K04 = keyof void; // never
|
||||
type K05 = keyof undefined; // never
|
||||
type K06 = keyof null; // never
|
||||
type K07 = keyof never; // never
|
||||
type K07 = keyof never; // string | number | symbol
|
||||
type K08 = keyof unknown; // never
|
||||
|
||||
type K10 = keyof Shape; // "name" | "width" | "height" | "visible"
|
||||
type K11 = keyof Shape[]; // "length" | "toString" | ...
|
||||
@ -1124,6 +1125,7 @@ declare type K04 = keyof void;
|
||||
declare type K05 = keyof undefined;
|
||||
declare type K06 = keyof null;
|
||||
declare type K07 = keyof never;
|
||||
declare type K08 = keyof unknown;
|
||||
declare type K10 = keyof Shape;
|
||||
declare type K11 = keyof Shape[];
|
||||
declare type K12 = keyof Dictionary<Shape>;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -76,8 +76,11 @@ type K06 = keyof null; // never
|
||||
>K06 : never
|
||||
>null : null
|
||||
|
||||
type K07 = keyof never; // never
|
||||
>K07 : never
|
||||
type K07 = keyof never; // string | number | symbol
|
||||
>K07 : string | number | symbol
|
||||
|
||||
type K08 = keyof unknown; // never
|
||||
>K08 : never
|
||||
|
||||
type K10 = keyof Shape; // "name" | "width" | "height" | "visible"
|
||||
>K10 : "name" | "width" | "height" | "visible"
|
||||
|
||||
@ -0,0 +1,42 @@
|
||||
interface TextChannel {
|
||||
id: string;
|
||||
type: 'text';
|
||||
phoneNumber: string;
|
||||
}
|
||||
|
||||
interface EmailChannel {
|
||||
id: string;
|
||||
type: 'email';
|
||||
addres: string;
|
||||
}
|
||||
|
||||
type Channel = TextChannel | EmailChannel;
|
||||
|
||||
export type ChannelType = Channel extends { type: infer R } ? R : never;
|
||||
|
||||
type Omit<T, K extends keyof T> = Pick<
|
||||
T,
|
||||
({ [P in keyof T]: P } & { [P in K]: never } & { [x: string]: never })[keyof T]
|
||||
>;
|
||||
|
||||
type ChannelOfType<T extends ChannelType, A = Channel> = A extends { type: T }
|
||||
? A
|
||||
: never;
|
||||
|
||||
|
||||
export type NewChannel<T extends Channel> = Pick<T, 'type'> &
|
||||
Partial<Omit<T, 'type' | 'id'>> & { localChannelId: string };
|
||||
|
||||
|
||||
export function makeNewChannel<T extends ChannelType>(type: T): NewChannel<ChannelOfType<T>> {
|
||||
const localChannelId = `blahblahblah`;
|
||||
return { type, localChannelId };
|
||||
}
|
||||
|
||||
const newTextChannel = makeNewChannel('text');
|
||||
// This should work
|
||||
newTextChannel.phoneNumber = '613-555-1234';
|
||||
|
||||
const newTextChannel2 : NewChannel<TextChannel> = makeNewChannel('text');
|
||||
// Compare with this, which ofc works.
|
||||
newTextChannel2.phoneNumber = '613-555-1234';
|
||||
@ -33,7 +33,8 @@ type K03 = keyof boolean; // "valueOf"
|
||||
type K04 = keyof void; // never
|
||||
type K05 = keyof undefined; // never
|
||||
type K06 = keyof null; // never
|
||||
type K07 = keyof never; // never
|
||||
type K07 = keyof never; // string | number | symbol
|
||||
type K08 = keyof unknown; // never
|
||||
|
||||
type K10 = keyof Shape; // "name" | "width" | "height" | "visible"
|
||||
type K11 = keyof Shape[]; // "length" | "toString" | ...
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user