Strengthen and unify negated type <-> filtering conditional workaround rule

This commit is contained in:
Wesley Wigham committed 2026-10-21 10:38:59 -07:00
1 parent 0c876d27c5
commit be90172a52
11 files changed
+1689 -32

No files matched your search

+7
View File
@@ -152,6 +152,11 @@ type CachedTypeKey struct {
typeId TypeId
}
type DistributiveInstantiationKey struct {
typeId TypeId
typeParameterId TypeId
}
// NarrowedTypeKey
type NarrowedTypeKey struct {
@@ -651,6 +656,7 @@ type Checker struct {
thisExpandoLocations map[*ast.Symbol]*ast.Node
subtypeReductionCache map[CacheHashKey][]*Type
cachedTypes map[CachedTypeKey]*Type
distributiveInstantiationCache map[DistributiveInstantiationKey]bool
cachedSignatures map[CachedSignatureKey]*Signature
undefinedProperties map[*ast.Symbol]*ast.Symbol
narrowedTypes map[NarrowedTypeKey]*Type
@@ -990,6 +996,7 @@ func NewChecker(program Program, tracer *Tracer) (*Checker, *sync.Mutex) {
c.thisExpandoLocations = make(map[*ast.Symbol]*ast.Node)
c.subtypeReductionCache = make(map[CacheHashKey][]*Type)
c.cachedTypes = make(map[CachedTypeKey]*Type)
c.distributiveInstantiationCache = make(map[DistributiveInstantiationKey]bool)
c.cachedSignatures = make(map[CachedSignatureKey]*Signature)
c.undefinedProperties = make(map[*ast.Symbol]*ast.Symbol)
c.narrowedTypes = make(map[NarrowedTypeKey]*Type)
+71 -32
View File
@@ -3616,8 +3616,8 @@ func (r *Relater) structuredTypeRelatedToWorker(source *Type, target *Type, repo
nameType := r.c.getNameTypeFromMappedType(targetType)
constraintType := r.c.getConstraintTypeFromMappedType(targetType)
var targetKeys *Type
isFiltering := nameType != nil && r.c.isMappedTypeWithKeyofConstraintDeclaration(targetType)
if isFiltering {
isRemappedKeyof := nameType != nil && r.c.isMappedTypeWithKeyofConstraintDeclaration(targetType)
if isRemappedKeyof {
// we need to get the apparent mappings and union them with the generic mappings, since some properties may be
// missing from the `constraintType` which will otherwise be mapped in the object
mappedKeys := r.c.getApparentMappedTypeKeys(nameType, targetType)
@@ -3628,15 +3628,14 @@ func (r *Relater) structuredTypeRelatedToWorker(source *Type, target *Type, repo
} else {
targetKeys = constraintType
}
if isFiltering && source.flags&TypeFlagsIntersection != 0 && r.someTypeRelatedToType(source, constraintType, false, intersectionState) == TernaryTrue {
// run the filter over `source` - some part of `source` is related to the constraint - if source's other elements end up filtering the same way the nameType does, the assignment can pass
// This effectively is a workaround for a lack of distributive conditional/distributive homomorphic mapped types relationship rules that uses execution of the distribution (even at higher order)
// to approximate a better result.
// See the homomorphic case in tsc\testdata\tests\cases\compiler\indexMappedTypeWithFilteredKeysWithNegation.ts
sourceInstantiatedTarget := r.c.instantiateType(nameType, newSimpleTypeMapper(r.c.getTypeParameterFromMappedType(targetType), source))
broadTargetKeys := r.c.getUnionType([]*Type{targetKeys, sourceInstantiatedTarget})
if r.isRelatedTo(source, broadTargetKeys, RecursionFlagsTarget, false) == TernaryTrue {
return TernaryTrue
if isRemappedKeyof && source.flags&TypeFlagsIntersection != 0 {
if filteredNameType := r.getOptionalSimplificationOfConditional(nameType); filteredNameType != nil && r.someTypeRelatedToType(source, constraintType, false, intersectionState) == TernaryTrue {
// Apply the filter only to a source already constrained to the mapped keys.
sourceInstantiatedTarget := r.c.instantiateType(filteredNameType, newSimpleTypeMapper(r.c.getTypeParameterFromMappedType(targetType), source))
broadTargetKeys := r.c.getUnionType([]*Type{targetKeys, sourceInstantiatedTarget})
if r.isRelatedTo(source, broadTargetKeys, RecursionFlagsTarget, false) == TernaryTrue {
return TernaryTrue
}
}
}
if r.isRelatedTo(source, targetKeys, RecursionFlagsTarget, reportErrors) == TernaryTrue {
@@ -4024,32 +4023,72 @@ func (r *Relater) structuredTypeRelatedToWorker(source *Type, target *Type, repo
return TernaryFalse
}
// TODO: Replace this heuristic with proper distributive conditional type relationship rules.
// For a distributive A extends B ? never : Q<A> without infer parameters, rewrite to Q<A & not B>.
// A definitely false check uses Q<A> unchanged; a definitely true check cannot use the rewrite.
// Otherwise B must be independent of A, and Q<never> must be never. The conditional already distributes,
// but we also probe whether Q<X | Y> agrees with Q<X> | Q<Y> before moving the union inside Q.
// See Repeated in indexMappedTypeWithFilteredKeysWithNegationErrors.ts, where moving the union
// inside `${A}-${A}` would lose the correlation between occurrences of A.
func (r *Relater) getOptionalSimplificationOfConditional(target *Type) *Type {
if target.flags&TypeFlagsConditional == 0 {
return nil
}
// TODO: This is a kludge for not having proper rules for relating distributive conditional types.
// We rewrite `A extends B ? never : Q<A>` into `Q<A & not B>` - provided that substitution
// actually changes it. This is not *particularly* accurate, but enables the common case of relating a filtering mapped type to the equivalent filtering negated type.
// See the non-homomorphic case in tsc\testdata\tests\cases\compiler\indexMappedTypeWithFilteredKeysWithNegation.ts for an example.
root := target.AsConditionalType().root
if root.isDistributive && r.c.getTrueTypeFromConditionalType(target).flags&TypeFlagsNever != 0 {
f := r.c.getFalseTypeFromConditionalType(target)
remappedFalseType := r.c.instantiateType(
r.c.getTypeFromTypeNode(root.node.FalseType),
r.c.combineTypeMappers(
newSimpleTypeMapper(
getNonDistributedTypeParameter(root.checkType),
r.c.getIntersectionType([]*Type{root.checkType, r.c.getNegatedType(root.extendsType)}),
),
target.AsConditionalType().mapper,
),
)
if remappedFalseType != f {
return remappedFalseType
}
conditional := target.AsConditionalType()
root := conditional.root
if !root.isDistributive || len(root.inferTypeParameters) != 0 || r.c.getTrueTypeFromConditionalType(target).flags&TypeFlagsNever == 0 {
return nil
}
return nil
permissiveExtendsType := r.c.getPermissiveInstantiation(conditional.extendsType)
skipTrue := !someType(r.c.getPermissiveInstantiation(conditional.checkType), func(t *Type) bool {
return r.c.isTypeAssignableTo(t, permissiveExtendsType)
})
if skipTrue {
return r.c.getFalseTypeFromConditionalType(target)
}
if r.c.isTypeAssignableTo(r.c.getRestrictiveInstantiation(conditional.checkType), r.c.getRestrictiveInstantiation(conditional.extendsType)) {
return nil
}
checkType := getNonDistributedTypeParameter(root.checkType)
if r.c.isTypeParameterPossiblyReferenced(checkType, root.node.ExtendsType) {
return nil
}
falseType := r.c.getTypeFromTypeNode(root.node.FalseType)
if !r.canDistributeTypeInstantiation(falseType, checkType) {
return nil
}
return r.c.instantiateType(
falseType,
r.c.combineTypeMappers(
newSimpleTypeMapper(checkType, r.c.getIntersectionType([]*Type{root.checkType, r.c.getNegatedType(root.extendsType)})),
conditional.mapper,
),
)
}
func (r *Relater) canDistributeTypeInstantiation(t *Type, typeParameter *Type) bool {
key := DistributiveInstantiationKey{typeId: t.id, typeParameterId: typeParameter.id}
if result, ok := r.c.distributiveInstantiationCache[key]; ok {
return result
}
// A recursive probe is unproven until the outer comparison completes.
r.c.distributiveInstantiationCache[key] = false
// Filtering can remove the entire check type, so the false branch must also preserve never.
if r.c.instantiateType(t, newSimpleTypeMapper(typeParameter, r.c.neverType)).flags&TypeFlagsNever == 0 {
return false
}
left := r.c.markerSuperType
right := r.c.markerOtherType
instantiate := func(argument *Type) *Type {
return r.c.getSimplifiedType(r.c.instantiateType(t, newSimpleTypeMapper(typeParameter, argument)), false /*writing*/)
}
// Require execution over unrelated union members to agree with distributing first.
// Use the existing identity comparison rather than introducing conditional relationship rules.
instantiatedUnion := instantiate(r.c.getUnionType([]*Type{left, right}))
unionOfInstantiations := r.c.getUnionType([]*Type{instantiate(left), instantiate(right)})
result := r.c.isTypeIdenticalTo(instantiatedUnion, unionOfInstantiations)
r.c.distributiveInstantiationCache[key] = result
return result
}
func (r *Relater) typeArgumentsRelatedTo(sources []*Type, targets []*Type, variances []VarianceFlags, reportErrors bool, intersectionState IntersectionState) Ternary {
@@ -39,6 +39,102 @@ const t1: T1 = ['b', 'c'];
type S1<T> = { [K in keyof T & string as K extends '_key' ? never : K]: 1 }[LocalExclude<keyof T & string, '_key'>];
type S2<T> = { [K in string & keyof T as K extends '_key' ? never : K]: 1 }[LocalExclude<string & keyof T, '_key'>];
type ConditionalExclude<T, U> = T extends U ? never : T;
function exclude<T, U>(value: T & not U): ConditionalExclude<T, U> {
return value;
}
type Identity<T> = T;
type AliasedExclude<T, U> = T extends U ? never : Identity<T>;
function aliasedExclude<T, U>(value: T & not U): AliasedExclude<T, U> {
return value;
}
function mappedExclude<T, U>(value: keyof T & not U): keyof ExcludeKey<T, U> {
return value;
}
type AliasedExcludeKey<T, U> = {
[K in keyof T as ConditionalExclude<K, U>]: T[K]
};
function aliasedMappedExclude<T, U>(value: keyof T & not U): keyof AliasedExcludeKey<T, U> {
return value;
}
type Prefixed<T extends string> = T extends "skip" ? never : `prefix${T}`;
function prefixed<T extends string>(value: `prefix${T & not "skip"}`): Prefixed<T> {
return value;
}
type WithSuffix<T extends string, S extends string> = T extends "skip" ? never : `prefix${T}:${S}`;
function withSuffix<T extends string, S extends string>(value: `prefix${T & not "skip"}:${S}`): WithSuffix<T, S> {
return value;
}
type Cased<T extends string> = Uppercase<`prefix${Lowercase<T>}`>;
type FilteredCased<T extends string> = T extends "skip" ? never : Cased<T>;
function cased<T extends string>(value: Cased<T & not "skip">): FilteredCased<T> {
return value;
}
type Alternatives<T extends string> = T extends "skip" ? never : `left${T}` | `right${Uppercase<T>}`;
function alternatives<T extends string>(
value: `left${T & not "skip"}` | `right${Uppercase<T & not "skip">}`
): Alternatives<T> {
return value;
}
type Branded<T> = T extends "skip" ? never : T & { brand: true };
function branded<T>(value: T & not "skip" & { brand: true }): Branded<T> {
return value;
}
interface Values {
a: 1;
b: 2;
skip: 3;
}
type Lookup<T extends keyof Values> = T extends "skip" ? never : Values[T];
function lookup<T extends keyof Values>(value: Values[T & not "skip"]): Lookup<T> {
return value;
}
type PrefixedKeys<T> = {
[K in keyof T & string as K extends "_key" ? never : `prefix${K}`]: T[K]
};
function prefixedKey<T>(value: `prefix${keyof T & string & not "_key"}`): keyof PrefixedKeys<T> {
return value;
}
type FunctionResult<T> = T extends string ? never : () => T;
function falseFunction<T>(value: () => [T]): FunctionResult<[T]> {
return value;
}
type TupleResult<T> = T extends string ? never : [T, T];
function falseTuple<T>(value: [[T], [T]]): TupleResult<[T]> {
return value;
}
type Nested<T extends string> = T extends "skip" ? never : ConditionalExclude<`prefix${T}`, "prefixomit">;
function nested<T extends string>(value: ConditionalExclude<`prefix${T & not "skip"}`, "prefixomit">): Nested<T> {
return value;
}
//// [indexMappedTypeWithFilteredKeysWithNegation.js]
@@ -48,3 +144,45 @@ function f(test, possibleKey) {
test[possibleKey];
}
const t1 = ['b', 'c'];
function exclude(value) {
return value;
}
function aliasedExclude(value) {
return value;
}
function mappedExclude(value) {
return value;
}
function aliasedMappedExclude(value) {
return value;
}
function prefixed(value) {
return value;
}
function withSuffix(value) {
return value;
}
function cased(value) {
return value;
}
function alternatives(value) {
return value;
}
function branded(value) {
return value;
}
function lookup(value) {
return value;
}
function prefixedKey(value) {
return value;
}
function falseFunction(value) {
return value;
}
function falseTuple(value) {
return value;
}
function nested(value) {
return value;
}
@@ -137,3 +137,334 @@ type S2<T> = { [K in string & keyof T as K extends '_key' ? never : K]: 1 }[Loca
>LocalExclude : Symbol(LocalExclude, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 18, 1))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 37, 8))
type ConditionalExclude<T, U> = T extends U ? never : T;
>ConditionalExclude : Symbol(ConditionalExclude, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 37, 116))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 39, 24))
>U : Symbol(U, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 39, 26))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 39, 24))
>U : Symbol(U, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 39, 26))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 39, 24))
function exclude<T, U>(value: T & not U): ConditionalExclude<T, U> {
>exclude : Symbol(exclude, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 39, 56))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 41, 17))
>U : Symbol(U, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 41, 19))
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 41, 23))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 41, 17))
>U : Symbol(U, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 41, 19))
>ConditionalExclude : Symbol(ConditionalExclude, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 37, 116))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 41, 17))
>U : Symbol(U, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 41, 19))
return value;
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 41, 23))
}
type Identity<T> = T;
>Identity : Symbol(Identity, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 43, 1))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 45, 14))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 45, 14))
type AliasedExclude<T, U> = T extends U ? never : Identity<T>;
>AliasedExclude : Symbol(AliasedExclude, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 45, 21))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 46, 20))
>U : Symbol(U, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 46, 22))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 46, 20))
>U : Symbol(U, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 46, 22))
>Identity : Symbol(Identity, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 43, 1))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 46, 20))
function aliasedExclude<T, U>(value: T & not U): AliasedExclude<T, U> {
>aliasedExclude : Symbol(aliasedExclude, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 46, 62))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 48, 24))
>U : Symbol(U, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 48, 26))
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 48, 30))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 48, 24))
>U : Symbol(U, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 48, 26))
>AliasedExclude : Symbol(AliasedExclude, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 45, 21))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 48, 24))
>U : Symbol(U, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 48, 26))
return value;
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 48, 30))
}
function mappedExclude<T, U>(value: keyof T & not U): keyof ExcludeKey<T, U> {
>mappedExclude : Symbol(mappedExclude, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 50, 1))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 52, 23))
>U : Symbol(U, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 52, 25))
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 52, 29))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 52, 23))
>U : Symbol(U, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 52, 25))
>ExcludeKey : Symbol(ExcludeKey, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 0, 0))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 52, 23))
>U : Symbol(U, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 52, 25))
return value;
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 52, 29))
}
type AliasedExcludeKey<T, U> = {
>AliasedExcludeKey : Symbol(AliasedExcludeKey, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 54, 1))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 56, 23))
>U : Symbol(U, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 56, 25))
[K in keyof T as ConditionalExclude<K, U>]: T[K]
>K : Symbol(K, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 57, 5))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 56, 23))
>ConditionalExclude : Symbol(ConditionalExclude, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 37, 116))
>K : Symbol(K, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 57, 5))
>U : Symbol(U, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 56, 25))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 56, 23))
>K : Symbol(K, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 57, 5))
};
function aliasedMappedExclude<T, U>(value: keyof T & not U): keyof AliasedExcludeKey<T, U> {
>aliasedMappedExclude : Symbol(aliasedMappedExclude, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 58, 2))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 60, 30))
>U : Symbol(U, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 60, 32))
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 60, 36))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 60, 30))
>U : Symbol(U, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 60, 32))
>AliasedExcludeKey : Symbol(AliasedExcludeKey, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 54, 1))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 60, 30))
>U : Symbol(U, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 60, 32))
return value;
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 60, 36))
}
type Prefixed<T extends string> = T extends "skip" ? never : `prefix${T}`;
>Prefixed : Symbol(Prefixed, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 62, 1))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 64, 14))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 64, 14))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 64, 14))
function prefixed<T extends string>(value: `prefix${T & not "skip"}`): Prefixed<T> {
>prefixed : Symbol(prefixed, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 64, 74))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 66, 18))
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 66, 36))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 66, 18))
>Prefixed : Symbol(Prefixed, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 62, 1))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 66, 18))
return value;
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 66, 36))
}
type WithSuffix<T extends string, S extends string> = T extends "skip" ? never : `prefix${T}:${S}`;
>WithSuffix : Symbol(WithSuffix, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 68, 1))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 70, 16))
>S : Symbol(S, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 70, 33))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 70, 16))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 70, 16))
>S : Symbol(S, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 70, 33))
function withSuffix<T extends string, S extends string>(value: `prefix${T & not "skip"}:${S}`): WithSuffix<T, S> {
>withSuffix : Symbol(withSuffix, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 70, 99))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 72, 20))
>S : Symbol(S, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 72, 37))
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 72, 56))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 72, 20))
>S : Symbol(S, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 72, 37))
>WithSuffix : Symbol(WithSuffix, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 68, 1))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 72, 20))
>S : Symbol(S, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 72, 37))
return value;
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 72, 56))
}
type Cased<T extends string> = Uppercase<`prefix${Lowercase<T>}`>;
>Cased : Symbol(Cased, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 74, 1))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 76, 11))
>Uppercase : Symbol(Uppercase, Decl(lib.es5.d.ts, --, --))
>Lowercase : Symbol(Lowercase, Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 76, 11))
type FilteredCased<T extends string> = T extends "skip" ? never : Cased<T>;
>FilteredCased : Symbol(FilteredCased, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 76, 66))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 77, 19))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 77, 19))
>Cased : Symbol(Cased, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 74, 1))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 77, 19))
function cased<T extends string>(value: Cased<T & not "skip">): FilteredCased<T> {
>cased : Symbol(cased, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 77, 75))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 79, 15))
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 79, 33))
>Cased : Symbol(Cased, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 74, 1))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 79, 15))
>FilteredCased : Symbol(FilteredCased, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 76, 66))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 79, 15))
return value;
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 79, 33))
}
type Alternatives<T extends string> = T extends "skip" ? never : `left${T}` | `right${Uppercase<T>}`;
>Alternatives : Symbol(Alternatives, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 81, 1))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 83, 18))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 83, 18))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 83, 18))
>Uppercase : Symbol(Uppercase, Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 83, 18))
function alternatives<T extends string>(
>alternatives : Symbol(alternatives, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 83, 101))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 85, 22))
value: `left${T & not "skip"}` | `right${Uppercase<T & not "skip">}`
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 85, 40))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 85, 22))
>Uppercase : Symbol(Uppercase, Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 85, 22))
): Alternatives<T> {
>Alternatives : Symbol(Alternatives, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 81, 1))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 85, 22))
return value;
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 85, 40))
}
type Branded<T> = T extends "skip" ? never : T & { brand: true };
>Branded : Symbol(Branded, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 89, 1))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 91, 13))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 91, 13))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 91, 13))
>brand : Symbol(brand, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 91, 50))
function branded<T>(value: T & not "skip" & { brand: true }): Branded<T> {
>branded : Symbol(branded, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 91, 65))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 93, 17))
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 93, 20))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 93, 17))
>brand : Symbol(brand, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 93, 45))
>Branded : Symbol(Branded, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 89, 1))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 93, 17))
return value;
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 93, 20))
}
interface Values {
>Values : Symbol(Values, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 95, 1))
a: 1;
>a : Symbol(Values.a, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 97, 18))
b: 2;
>b : Symbol(Values.b, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 98, 9))
skip: 3;
>skip : Symbol(Values.skip, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 99, 9))
}
type Lookup<T extends keyof Values> = T extends "skip" ? never : Values[T];
>Lookup : Symbol(Lookup, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 101, 1))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 103, 12))
>Values : Symbol(Values, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 95, 1))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 103, 12))
>Values : Symbol(Values, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 95, 1))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 103, 12))
function lookup<T extends keyof Values>(value: Values[T & not "skip"]): Lookup<T> {
>lookup : Symbol(lookup, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 103, 75))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 105, 16))
>Values : Symbol(Values, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 95, 1))
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 105, 40))
>Values : Symbol(Values, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 95, 1))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 105, 16))
>Lookup : Symbol(Lookup, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 101, 1))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 105, 16))
return value;
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 105, 40))
}
type PrefixedKeys<T> = {
>PrefixedKeys : Symbol(PrefixedKeys, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 107, 1))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 109, 18))
[K in keyof T & string as K extends "_key" ? never : `prefix${K}`]: T[K]
>K : Symbol(K, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 110, 5))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 109, 18))
>K : Symbol(K, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 110, 5))
>K : Symbol(K, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 110, 5))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 109, 18))
>K : Symbol(K, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 110, 5))
};
function prefixedKey<T>(value: `prefix${keyof T & string & not "_key"}`): keyof PrefixedKeys<T> {
>prefixedKey : Symbol(prefixedKey, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 111, 2))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 113, 21))
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 113, 24))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 113, 21))
>PrefixedKeys : Symbol(PrefixedKeys, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 107, 1))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 113, 21))
return value;
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 113, 24))
}
type FunctionResult<T> = T extends string ? never : () => T;
>FunctionResult : Symbol(FunctionResult, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 115, 1))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 117, 20))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 117, 20))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 117, 20))
function falseFunction<T>(value: () => [T]): FunctionResult<[T]> {
>falseFunction : Symbol(falseFunction, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 117, 60))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 119, 23))
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 119, 26))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 119, 23))
>FunctionResult : Symbol(FunctionResult, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 115, 1))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 119, 23))
return value;
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 119, 26))
}
type TupleResult<T> = T extends string ? never : [T, T];
>TupleResult : Symbol(TupleResult, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 121, 1))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 123, 17))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 123, 17))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 123, 17))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 123, 17))
function falseTuple<T>(value: [[T], [T]]): TupleResult<[T]> {
>falseTuple : Symbol(falseTuple, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 123, 56))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 125, 20))
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 125, 23))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 125, 20))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 125, 20))
>TupleResult : Symbol(TupleResult, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 121, 1))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 125, 20))
return value;
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 125, 23))
}
type Nested<T extends string> = T extends "skip" ? never : ConditionalExclude<`prefix${T}`, "prefixomit">;
>Nested : Symbol(Nested, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 127, 1))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 129, 12))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 129, 12))
>ConditionalExclude : Symbol(ConditionalExclude, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 37, 116))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 129, 12))
function nested<T extends string>(value: ConditionalExclude<`prefix${T & not "skip"}`, "prefixomit">): Nested<T> {
>nested : Symbol(nested, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 129, 106))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 131, 16))
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 131, 34))
>ConditionalExclude : Symbol(ConditionalExclude, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 37, 116))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 131, 16))
>Nested : Symbol(Nested, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 127, 1))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 131, 16))
return value;
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegation.ts, 131, 34))
}
@@ -79,3 +79,184 @@ type S1<T> = { [K in keyof T & string as K extends '_key' ? never : K]: 1 }[Loca
type S2<T> = { [K in string & keyof T as K extends '_key' ? never : K]: 1 }[LocalExclude<string & keyof T, '_key'>];
>S2 : S2<T>
type ConditionalExclude<T, U> = T extends U ? never : T;
>ConditionalExclude : ConditionalExclude<T, U>
function exclude<T, U>(value: T & not U): ConditionalExclude<T, U> {
>exclude : <T, U>(value: T & not U) => ConditionalExclude<T, U>
>value : T & not U
return value;
>value : T & not U
}
type Identity<T> = T;
>Identity : T
type AliasedExclude<T, U> = T extends U ? never : Identity<T>;
>AliasedExclude : AliasedExclude<T, U>
function aliasedExclude<T, U>(value: T & not U): AliasedExclude<T, U> {
>aliasedExclude : <T, U>(value: T & not U) => AliasedExclude<T, U>
>value : T & not U
return value;
>value : T & not U
}
function mappedExclude<T, U>(value: keyof T & not U): keyof ExcludeKey<T, U> {
>mappedExclude : <T, U>(value: keyof T & not U) => keyof ExcludeKey<T, U>
>value : keyof T & not U
return value;
>value : keyof T & not U
}
type AliasedExcludeKey<T, U> = {
>AliasedExcludeKey : AliasedExcludeKey<T, U>
[K in keyof T as ConditionalExclude<K, U>]: T[K]
};
function aliasedMappedExclude<T, U>(value: keyof T & not U): keyof AliasedExcludeKey<T, U> {
>aliasedMappedExclude : <T, U>(value: keyof T & not U) => keyof AliasedExcludeKey<T, U>
>value : keyof T & not U
return value;
>value : keyof T & not U
}
type Prefixed<T extends string> = T extends "skip" ? never : `prefix${T}`;
>Prefixed : Prefixed<T>
function prefixed<T extends string>(value: `prefix${T & not "skip"}`): Prefixed<T> {
>prefixed : <T extends string>(value: `prefix${T & not "skip"}`) => Prefixed<T>
>value : `prefix${T & not "skip"}`
return value;
>value : `prefix${T & not "skip"}`
}
type WithSuffix<T extends string, S extends string> = T extends "skip" ? never : `prefix${T}:${S}`;
>WithSuffix : WithSuffix<T, S>
function withSuffix<T extends string, S extends string>(value: `prefix${T & not "skip"}:${S}`): WithSuffix<T, S> {
>withSuffix : <T extends string, S extends string>(value: `prefix${T & not "skip"}:${S}`) => WithSuffix<T, S>
>value : `prefix${T & not "skip"}:${S}`
return value;
>value : `prefix${T & not "skip"}:${S}`
}
type Cased<T extends string> = Uppercase<`prefix${Lowercase<T>}`>;
>Cased : `PREFIX${Uppercase<Lowercase<T>>}`
type FilteredCased<T extends string> = T extends "skip" ? never : Cased<T>;
>FilteredCased : FilteredCased<T>
function cased<T extends string>(value: Cased<T & not "skip">): FilteredCased<T> {
>cased : <T extends string>(value: Cased<T & not "skip">) => FilteredCased<T>
>value : `PREFIX${Uppercase<Lowercase<T & not "skip">>}`
return value;
>value : `PREFIX${Uppercase<Lowercase<T & not "skip">>}`
}
type Alternatives<T extends string> = T extends "skip" ? never : `left${T}` | `right${Uppercase<T>}`;
>Alternatives : Alternatives<T>
function alternatives<T extends string>(
>alternatives : <T extends string>(value: `left${T & not "skip"}` | `right${Uppercase<T & not "skip">}`) => Alternatives<T>
value: `left${T & not "skip"}` | `right${Uppercase<T & not "skip">}`
>value : `left${T & not "skip"}` | `right${Uppercase<T & not "skip">}`
): Alternatives<T> {
return value;
>value : `left${T & not "skip"}` | `right${Uppercase<T & not "skip">}`
}
type Branded<T> = T extends "skip" ? never : T & { brand: true };
>Branded : Branded<T>
>brand : true
>true : true
function branded<T>(value: T & not "skip" & { brand: true }): Branded<T> {
>branded : <T>(value: T & not "skip" & { brand: true; }) => Branded<T>
>value : T & not "skip" & { brand: true; }
>brand : true
>true : true
return value;
>value : T & not "skip" & { brand: true; }
}
interface Values {
a: 1;
>a : 1
b: 2;
>b : 2
skip: 3;
>skip : 3
}
type Lookup<T extends keyof Values> = T extends "skip" ? never : Values[T];
>Lookup : Lookup<T>
function lookup<T extends keyof Values>(value: Values[T & not "skip"]): Lookup<T> {
>lookup : <T extends keyof Values>(value: Values[T & not "skip"]) => Lookup<T>
>value : Values[T & not "skip"]
return value;
>value : Values[T & not "skip"]
}
type PrefixedKeys<T> = {
>PrefixedKeys : PrefixedKeys<T>
[K in keyof T & string as K extends "_key" ? never : `prefix${K}`]: T[K]
};
function prefixedKey<T>(value: `prefix${keyof T & string & not "_key"}`): keyof PrefixedKeys<T> {
>prefixedKey : <T>(value: `prefix${keyof T & string & not "_key"}`) => keyof PrefixedKeys<T>
>value : `prefix${keyof T & string & not "_key"}`
return value;
>value : `prefix${keyof T & string & not "_key"}`
}
type FunctionResult<T> = T extends string ? never : () => T;
>FunctionResult : FunctionResult<T>
function falseFunction<T>(value: () => [T]): FunctionResult<[T]> {
>falseFunction : <T>(value: () => [T]) => FunctionResult<[T]>
>value : () => [T]
return value;
>value : () => [T]
}
type TupleResult<T> = T extends string ? never : [T, T];
>TupleResult : TupleResult<T>
function falseTuple<T>(value: [[T], [T]]): TupleResult<[T]> {
>falseTuple : <T>(value: [[T], [T]]) => TupleResult<[T]>
>value : [[T], [T]]
return value;
>value : [[T], [T]]
}
type Nested<T extends string> = T extends "skip" ? never : ConditionalExclude<`prefix${T}`, "prefixomit">;
>Nested : Nested<T>
function nested<T extends string>(value: ConditionalExclude<`prefix${T & not "skip"}`, "prefixomit">): Nested<T> {
>nested : <T extends string>(value: ConditionalExclude<`prefix${T & not "skip"}`, "prefixomit">) => Nested<T>
>value : ConditionalExclude<`prefix${T & not "skip"}`, "prefixomit">
return value;
>value : ConditionalExclude<`prefix${T & not "skip"}`, "prefixomit">
}
@@ -0,0 +1,148 @@
indexMappedTypeWithFilteredKeysWithNegationErrors.ts(5,5): error TS2322: Type '() => never' is not assignable to type 'FilteredFunction<T>'.
indexMappedTypeWithFilteredKeysWithNegationErrors.ts(14,5): error TS2322: Type '[T & not false & not true, T & not false & not true]' is not assignable to type 'FilteredPair<T>'.
indexMappedTypeWithFilteredKeysWithNegationErrors.ts(24,5): error TS2322: Type 'T & not Omitted<T>' is not assignable to type 'DependentFilter<T>'.
indexMappedTypeWithFilteredKeysWithNegationErrors.ts(33,5): error TS2322: Type 'T' is not assignable to type 'Filter<T, U>'.
indexMappedTypeWithFilteredKeysWithNegationErrors.ts(42,5): error TS2322: Type 'keyof T & not "c"' is not assignable to type 'keyof FilteredKeys<T>'.
Type 'keyof T & not "c"' is not assignable to type 'K extends "b" ? never : K'.
indexMappedTypeWithFilteredKeysWithNegationErrors.ts(47,5): error TS2322: Type 'string & not "b"' is not assignable to type 'keyof FilteredKeys<T>'.
Type 'string & not "b"' is not assignable to type 'K extends "b" ? never : K'.
indexMappedTypeWithFilteredKeysWithNegationErrors.ts(58,5): error TS2322: Type 'keyof T & not "c"' is not assignable to type 'keyof RemappedKeys<T>'.
Type 'keyof T & not "c"' is not assignable to type '"a" | "b" extends K ? K : never'.
indexMappedTypeWithFilteredKeysWithNegationErrors.ts(67,5): error TS2322: Type 'keyof T & not Omitted<keyof T>' is not assignable to type 'keyof DependentKeys<T>'.
Type 'keyof T & not Omitted<keyof T>' is not assignable to type 'K extends Omitted<K> ? never : K'.
indexMappedTypeWithFilteredKeysWithNegationErrors.ts(74,5): error TS2322: Type '`${T & not "skip"}-${T & not "skip"}`' is not assignable to type 'Repeated<T>'.
Type '`${string}-${string}`' is not assignable to type 'Repeated<T>'.
indexMappedTypeWithFilteredKeysWithNegationErrors.ts(83,5): error TS2322: Type 'T & not string & { value: T & not string; }' is not assignable to type 'Correlated<T>'.
indexMappedTypeWithFilteredKeysWithNegationErrors.ts(88,5): error TS2322: Type '() => string & { value: T; }' is not assignable to type 'never'.
indexMappedTypeWithFilteredKeysWithNegationErrors.ts(93,5): error TS2322: Type '() => string | [T]' is not assignable to type '() => [T]'.
Type 'string | [T]' is not assignable to type '[T]'.
Type 'string' is not assignable to type '[T]'.
==== indexMappedTypeWithFilteredKeysWithNegationErrors.ts (12 errors) ====
type FilteredFunction<T> = T extends string ? never : () => T;
function makeFunction<T>(): FilteredFunction<T> {
// When T is string, FilteredFunction<T> is never, but this still returns a function.
return () => { throw new Error(); };
~~~~~~
!!! error TS2322: Type '() => never' is not assignable to type 'FilteredFunction<T>'.
}
const impossibleFunction: never = makeFunction<string>();
type FilteredPair<T> = T extends boolean ? never : [T, T];
function makePair<T>(value: [T & not boolean, T & not boolean]): FilteredPair<T> {
// The tuple elements can come from different union members, unlike the distributed FilteredPair<T>.
return value;
~~~~~~
!!! error TS2322: Type '[T & not false & not true, T & not false & not true]' is not assignable to type 'FilteredPair<T>'.
}
const homogeneousPair: [string, string] | [number, number] = makePair<string | number>(["a", 1]);
type Omitted<T> = "a" extends T ? never : "b";
type DependentFilter<T> = T extends Omitted<T> ? never : T;
function dependentFilter<T>(value: T & not Omitted<T>): DependentFilter<T> {
// For T = "a" | "b", Omitted<T> is never, so value can be "b", but DependentFilter<T> is "a".
return value;
~~~~~~
!!! error TS2322: Type 'T & not Omitted<T>' is not assignable to type 'DependentFilter<T>'.
}
const onlyA: "a" = dependentFilter<"a" | "b">("b");
type Filter<T, U> = T extends U ? never : T;
function unfiltered<T, U>(value: T): Filter<T, U> {
// value can still belong to U, whose members are excluded from Filter<T, U>.
return value;
~~~~~~
!!! error TS2322: Type 'T' is not assignable to type 'Filter<T, U>'.
}
type FilteredKeys<T> = {
[K in keyof T as K extends "b" ? never : K]: T[K]
};
function excludedKey<T>(value: keyof T & not "c"): keyof FilteredKeys<T> {
// Excluding "c" does not prevent value from being the filtered-out key "b".
return value;
~~~~~~
!!! error TS2322: Type 'keyof T & not "c"' is not assignable to type 'keyof FilteredKeys<T>'.
!!! error TS2322: Type 'keyof T & not "c"' is not assignable to type 'K extends "b" ? never : K'.
}
function unrelatedKey<T>(value: string & not "b"): keyof FilteredKeys<T> {
// value is not constrained to keyof T and may name a property T does not have.
return value;
~~~~~~
!!! error TS2322: Type 'string & not "b"' is not assignable to type 'keyof FilteredKeys<T>'.
!!! error TS2322: Type 'string & not "b"' is not assignable to type 'K extends "b" ? never : K'.
}
type RemappedKeys<T> = {
[K in keyof T as ("a" | "b") extends K ? K : never]: T[K]
};
function remappedKey<T extends { a: unknown; b: unknown }>(
value: keyof T & not "c"
): keyof RemappedKeys<T> {
// For T with just "a" and "b", neither key accepts their union, so no keys survive.
return value;
~~~~~~
!!! error TS2322: Type 'keyof T & not "c"' is not assignable to type 'keyof RemappedKeys<T>'.
!!! error TS2322: Type 'keyof T & not "c"' is not assignable to type '"a" | "b" extends K ? K : never'.
}
type DependentKeys<T> = {
[K in keyof T as K extends Omitted<K> ? never : K]: T[K]
};
function dependentKey<T>(value: keyof T & not Omitted<keyof T>): keyof DependentKeys<T> {
// For T = { a: number; b: number }, value can be "b", but only "a" survives the per-key filter.
return value;
~~~~~~
!!! error TS2322: Type 'keyof T & not Omitted<keyof T>' is not assignable to type 'keyof DependentKeys<T>'.
!!! error TS2322: Type 'keyof T & not Omitted<keyof T>' is not assignable to type 'K extends Omitted<K> ? never : K'.
}
type Repeated<T extends string> = T extends "skip" ? never : `${T}-${T}`;
function repeated<T extends string>(value: `${T & not "skip"}-${T & not "skip"}`): Repeated<T> {
// value can mix different union members in its two T positions, unlike Repeated<T>.
return value;
~~~~~~
!!! error TS2322: Type '`${T & not "skip"}-${T & not "skip"}`' is not assignable to type 'Repeated<T>'.
!!! error TS2322: Type '`${string}-${string}`' is not assignable to type 'Repeated<T>'.
}
const same: "a-a" | "b-b" = repeated<"a" | "b">("a-b");
type Correlated<T> = T extends string ? never : T & { value: T };
function correlated<T>(value: T & not string & { value: T & not string }): Correlated<T> {
// The outer T and the value property's T can come from different union members, unlike Correlated<T>.
return value;
~~~~~~
!!! error TS2322: Type 'T & not string & { value: T & not string; }' is not assignable to type 'Correlated<T>'.
}
function trueFunction<T>(value: () => (string & { value: T })): FilteredFunction<string & { value: T }> {
// The check type always extends string, so the result is never, not a function.
return value;
~~~~~~
!!! error TS2322: Type '() => string & { value: T; }' is not assignable to type 'never'.
}
function unionFunction<T>(value: () => ([T] | string)): FilteredFunction<[T] | string> {
// The string union member is filtered out, so the returned function must return [T], not string.
return value;
~~~~~~
!!! error TS2322: Type '() => string | [T]' is not assignable to type '() => [T]'.
!!! error TS2322: Type 'string | [T]' is not assignable to type '[T]'.
!!! error TS2322: Type 'string' is not assignable to type '[T]'.
}
@@ -0,0 +1,153 @@
//// [tests/cases/compiler/indexMappedTypeWithFilteredKeysWithNegationErrors.ts] ////
//// [indexMappedTypeWithFilteredKeysWithNegationErrors.ts]
type FilteredFunction<T> = T extends string ? never : () => T;
function makeFunction<T>(): FilteredFunction<T> {
// When T is string, FilteredFunction<T> is never, but this still returns a function.
return () => { throw new Error(); };
}
const impossibleFunction: never = makeFunction<string>();
type FilteredPair<T> = T extends boolean ? never : [T, T];
function makePair<T>(value: [T & not boolean, T & not boolean]): FilteredPair<T> {
// The tuple elements can come from different union members, unlike the distributed FilteredPair<T>.
return value;
}
const homogeneousPair: [string, string] | [number, number] = makePair<string | number>(["a", 1]);
type Omitted<T> = "a" extends T ? never : "b";
type DependentFilter<T> = T extends Omitted<T> ? never : T;
function dependentFilter<T>(value: T & not Omitted<T>): DependentFilter<T> {
// For T = "a" | "b", Omitted<T> is never, so value can be "b", but DependentFilter<T> is "a".
return value;
}
const onlyA: "a" = dependentFilter<"a" | "b">("b");
type Filter<T, U> = T extends U ? never : T;
function unfiltered<T, U>(value: T): Filter<T, U> {
// value can still belong to U, whose members are excluded from Filter<T, U>.
return value;
}
type FilteredKeys<T> = {
[K in keyof T as K extends "b" ? never : K]: T[K]
};
function excludedKey<T>(value: keyof T & not "c"): keyof FilteredKeys<T> {
// Excluding "c" does not prevent value from being the filtered-out key "b".
return value;
}
function unrelatedKey<T>(value: string & not "b"): keyof FilteredKeys<T> {
// value is not constrained to keyof T and may name a property T does not have.
return value;
}
type RemappedKeys<T> = {
[K in keyof T as ("a" | "b") extends K ? K : never]: T[K]
};
function remappedKey<T extends { a: unknown; b: unknown }>(
value: keyof T & not "c"
): keyof RemappedKeys<T> {
// For T with just "a" and "b", neither key accepts their union, so no keys survive.
return value;
}
type DependentKeys<T> = {
[K in keyof T as K extends Omitted<K> ? never : K]: T[K]
};
function dependentKey<T>(value: keyof T & not Omitted<keyof T>): keyof DependentKeys<T> {
// For T = { a: number; b: number }, value can be "b", but only "a" survives the per-key filter.
return value;
}
type Repeated<T extends string> = T extends "skip" ? never : `${T}-${T}`;
function repeated<T extends string>(value: `${T & not "skip"}-${T & not "skip"}`): Repeated<T> {
// value can mix different union members in its two T positions, unlike Repeated<T>.
return value;
}
const same: "a-a" | "b-b" = repeated<"a" | "b">("a-b");
type Correlated<T> = T extends string ? never : T & { value: T };
function correlated<T>(value: T & not string & { value: T & not string }): Correlated<T> {
// The outer T and the value property's T can come from different union members, unlike Correlated<T>.
return value;
}
function trueFunction<T>(value: () => (string & { value: T })): FilteredFunction<string & { value: T }> {
// The check type always extends string, so the result is never, not a function.
return value;
}
function unionFunction<T>(value: () => ([T] | string)): FilteredFunction<[T] | string> {
// The string union member is filtered out, so the returned function must return [T], not string.
return value;
}
//// [indexMappedTypeWithFilteredKeysWithNegationErrors.js]
"use strict";
function makeFunction() {
// When T is string, FilteredFunction<T> is never, but this still returns a function.
return () => { throw new Error(); };
}
const impossibleFunction = makeFunction();
function makePair(value) {
// The tuple elements can come from different union members, unlike the distributed FilteredPair<T>.
return value;
}
const homogeneousPair = makePair(["a", 1]);
function dependentFilter(value) {
// For T = "a" | "b", Omitted<T> is never, so value can be "b", but DependentFilter<T> is "a".
return value;
}
const onlyA = dependentFilter("b");
function unfiltered(value) {
// value can still belong to U, whose members are excluded from Filter<T, U>.
return value;
}
function excludedKey(value) {
// Excluding "c" does not prevent value from being the filtered-out key "b".
return value;
}
function unrelatedKey(value) {
// value is not constrained to keyof T and may name a property T does not have.
return value;
}
function remappedKey(value) {
// For T with just "a" and "b", neither key accepts their union, so no keys survive.
return value;
}
function dependentKey(value) {
// For T = { a: number; b: number }, value can be "b", but only "a" survives the per-key filter.
return value;
}
function repeated(value) {
// value can mix different union members in its two T positions, unlike Repeated<T>.
return value;
}
const same = repeated("a-b");
function correlated(value) {
// The outer T and the value property's T can come from different union members, unlike Correlated<T>.
return value;
}
function trueFunction(value) {
// The check type always extends string, so the result is never, not a function.
return value;
}
function unionFunction(value) {
// The string union member is filtered out, so the returned function must return [T], not string.
return value;
}
@@ -0,0 +1,283 @@
//// [tests/cases/compiler/indexMappedTypeWithFilteredKeysWithNegationErrors.ts] ////
=== indexMappedTypeWithFilteredKeysWithNegationErrors.ts ===
type FilteredFunction<T> = T extends string ? never : () => T;
>FilteredFunction : Symbol(FilteredFunction, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 0, 0))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 0, 22))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 0, 22))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 0, 22))
function makeFunction<T>(): FilteredFunction<T> {
>makeFunction : Symbol(makeFunction, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 0, 62))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 2, 22))
>FilteredFunction : Symbol(FilteredFunction, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 0, 0))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 2, 22))
// When T is string, FilteredFunction<T> is never, but this still returns a function.
return () => { throw new Error(); };
>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2022.error.d.ts, --, --))
}
const impossibleFunction: never = makeFunction<string>();
>impossibleFunction : Symbol(impossibleFunction, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 7, 5))
>makeFunction : Symbol(makeFunction, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 0, 62))
type FilteredPair<T> = T extends boolean ? never : [T, T];
>FilteredPair : Symbol(FilteredPair, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 7, 57))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 9, 18))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 9, 18))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 9, 18))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 9, 18))
function makePair<T>(value: [T & not boolean, T & not boolean]): FilteredPair<T> {
>makePair : Symbol(makePair, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 9, 58))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 11, 18))
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 11, 21))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 11, 18))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 11, 18))
>FilteredPair : Symbol(FilteredPair, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 7, 57))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 11, 18))
// The tuple elements can come from different union members, unlike the distributed FilteredPair<T>.
return value;
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 11, 21))
}
const homogeneousPair: [string, string] | [number, number] = makePair<string | number>(["a", 1]);
>homogeneousPair : Symbol(homogeneousPair, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 16, 5))
>makePair : Symbol(makePair, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 9, 58))
type Omitted<T> = "a" extends T ? never : "b";
>Omitted : Symbol(Omitted, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 16, 97))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 18, 13))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 18, 13))
type DependentFilter<T> = T extends Omitted<T> ? never : T;
>DependentFilter : Symbol(DependentFilter, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 18, 46))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 19, 21))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 19, 21))
>Omitted : Symbol(Omitted, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 16, 97))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 19, 21))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 19, 21))
function dependentFilter<T>(value: T & not Omitted<T>): DependentFilter<T> {
>dependentFilter : Symbol(dependentFilter, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 19, 59))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 21, 25))
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 21, 28))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 21, 25))
>Omitted : Symbol(Omitted, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 16, 97))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 21, 25))
>DependentFilter : Symbol(DependentFilter, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 18, 46))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 21, 25))
// For T = "a" | "b", Omitted<T> is never, so value can be "b", but DependentFilter<T> is "a".
return value;
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 21, 28))
}
const onlyA: "a" = dependentFilter<"a" | "b">("b");
>onlyA : Symbol(onlyA, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 26, 5))
>dependentFilter : Symbol(dependentFilter, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 19, 59))
type Filter<T, U> = T extends U ? never : T;
>Filter : Symbol(Filter, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 26, 51))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 28, 12))
>U : Symbol(U, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 28, 14))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 28, 12))
>U : Symbol(U, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 28, 14))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 28, 12))
function unfiltered<T, U>(value: T): Filter<T, U> {
>unfiltered : Symbol(unfiltered, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 28, 44))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 30, 20))
>U : Symbol(U, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 30, 22))
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 30, 26))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 30, 20))
>Filter : Symbol(Filter, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 26, 51))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 30, 20))
>U : Symbol(U, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 30, 22))
// value can still belong to U, whose members are excluded from Filter<T, U>.
return value;
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 30, 26))
}
type FilteredKeys<T> = {
>FilteredKeys : Symbol(FilteredKeys, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 33, 1))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 35, 18))
[K in keyof T as K extends "b" ? never : K]: T[K]
>K : Symbol(K, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 36, 5))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 35, 18))
>K : Symbol(K, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 36, 5))
>K : Symbol(K, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 36, 5))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 35, 18))
>K : Symbol(K, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 36, 5))
};
function excludedKey<T>(value: keyof T & not "c"): keyof FilteredKeys<T> {
>excludedKey : Symbol(excludedKey, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 37, 2))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 39, 21))
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 39, 24))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 39, 21))
>FilteredKeys : Symbol(FilteredKeys, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 33, 1))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 39, 21))
// Excluding "c" does not prevent value from being the filtered-out key "b".
return value;
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 39, 24))
}
function unrelatedKey<T>(value: string & not "b"): keyof FilteredKeys<T> {
>unrelatedKey : Symbol(unrelatedKey, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 42, 1))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 44, 22))
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 44, 25))
>FilteredKeys : Symbol(FilteredKeys, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 33, 1))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 44, 22))
// value is not constrained to keyof T and may name a property T does not have.
return value;
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 44, 25))
}
type RemappedKeys<T> = {
>RemappedKeys : Symbol(RemappedKeys, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 47, 1))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 49, 18))
[K in keyof T as ("a" | "b") extends K ? K : never]: T[K]
>K : Symbol(K, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 50, 5))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 49, 18))
>K : Symbol(K, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 50, 5))
>K : Symbol(K, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 50, 5))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 49, 18))
>K : Symbol(K, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 50, 5))
};
function remappedKey<T extends { a: unknown; b: unknown }>(
>remappedKey : Symbol(remappedKey, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 51, 2))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 53, 21))
>a : Symbol(a, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 53, 32))
>b : Symbol(b, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 53, 44))
value: keyof T & not "c"
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 53, 59))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 53, 21))
): keyof RemappedKeys<T> {
>RemappedKeys : Symbol(RemappedKeys, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 47, 1))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 53, 21))
// For T with just "a" and "b", neither key accepts their union, so no keys survive.
return value;
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 53, 59))
}
type DependentKeys<T> = {
>DependentKeys : Symbol(DependentKeys, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 58, 1))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 60, 19))
[K in keyof T as K extends Omitted<K> ? never : K]: T[K]
>K : Symbol(K, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 61, 5))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 60, 19))
>K : Symbol(K, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 61, 5))
>Omitted : Symbol(Omitted, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 16, 97))
>K : Symbol(K, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 61, 5))
>K : Symbol(K, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 61, 5))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 60, 19))
>K : Symbol(K, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 61, 5))
};
function dependentKey<T>(value: keyof T & not Omitted<keyof T>): keyof DependentKeys<T> {
>dependentKey : Symbol(dependentKey, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 62, 2))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 64, 22))
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 64, 25))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 64, 22))
>Omitted : Symbol(Omitted, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 16, 97))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 64, 22))
>DependentKeys : Symbol(DependentKeys, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 58, 1))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 64, 22))
// For T = { a: number; b: number }, value can be "b", but only "a" survives the per-key filter.
return value;
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 64, 25))
}
type Repeated<T extends string> = T extends "skip" ? never : `${T}-${T}`;
>Repeated : Symbol(Repeated, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 67, 1))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 69, 14))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 69, 14))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 69, 14))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 69, 14))
function repeated<T extends string>(value: `${T & not "skip"}-${T & not "skip"}`): Repeated<T> {
>repeated : Symbol(repeated, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 69, 73))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 71, 18))
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 71, 36))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 71, 18))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 71, 18))
>Repeated : Symbol(Repeated, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 67, 1))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 71, 18))
// value can mix different union members in its two T positions, unlike Repeated<T>.
return value;
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 71, 36))
}
const same: "a-a" | "b-b" = repeated<"a" | "b">("a-b");
>same : Symbol(same, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 76, 5))
>repeated : Symbol(repeated, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 69, 73))
type Correlated<T> = T extends string ? never : T & { value: T };
>Correlated : Symbol(Correlated, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 76, 55))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 78, 16))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 78, 16))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 78, 16))
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 78, 53))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 78, 16))
function correlated<T>(value: T & not string & { value: T & not string }): Correlated<T> {
>correlated : Symbol(correlated, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 78, 65))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 80, 20))
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 80, 23))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 80, 20))
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 80, 48))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 80, 20))
>Correlated : Symbol(Correlated, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 76, 55))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 80, 20))
// The outer T and the value property's T can come from different union members, unlike Correlated<T>.
return value;
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 80, 23))
}
function trueFunction<T>(value: () => (string & { value: T })): FilteredFunction<string & { value: T }> {
>trueFunction : Symbol(trueFunction, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 83, 1))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 85, 22))
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 85, 25))
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 85, 49))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 85, 22))
>FilteredFunction : Symbol(FilteredFunction, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 0, 0))
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 85, 91))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 85, 22))
// The check type always extends string, so the result is never, not a function.
return value;
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 85, 25))
}
function unionFunction<T>(value: () => ([T] | string)): FilteredFunction<[T] | string> {
>unionFunction : Symbol(unionFunction, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 88, 1))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 90, 23))
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 90, 26))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 90, 23))
>FilteredFunction : Symbol(FilteredFunction, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 0, 0))
>T : Symbol(T, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 90, 23))
// The string union member is filtered out, so the returned function must return [T], not string.
return value;
>value : Symbol(value, Decl(indexMappedTypeWithFilteredKeysWithNegationErrors.ts, 90, 26))
}
@@ -0,0 +1,185 @@
//// [tests/cases/compiler/indexMappedTypeWithFilteredKeysWithNegationErrors.ts] ////
=== indexMappedTypeWithFilteredKeysWithNegationErrors.ts ===
type FilteredFunction<T> = T extends string ? never : () => T;
>FilteredFunction : FilteredFunction<T>
function makeFunction<T>(): FilteredFunction<T> {
>makeFunction : <T>() => FilteredFunction<T>
// When T is string, FilteredFunction<T> is never, but this still returns a function.
return () => { throw new Error(); };
>() => { throw new Error(); } : () => never
>new Error() : Error
>Error : ErrorConstructor
}
const impossibleFunction: never = makeFunction<string>();
>impossibleFunction : never
>makeFunction<string>() : never
>makeFunction : <T>() => FilteredFunction<T>
type FilteredPair<T> = T extends boolean ? never : [T, T];
>FilteredPair : FilteredPair<T>
function makePair<T>(value: [T & not boolean, T & not boolean]): FilteredPair<T> {
>makePair : <T>(value: [T & not boolean, T & not boolean]) => FilteredPair<T>
>value : [T & not false & not true, T & not false & not true]
// The tuple elements can come from different union members, unlike the distributed FilteredPair<T>.
return value;
>value : [T & not false & not true, T & not false & not true]
}
const homogeneousPair: [string, string] | [number, number] = makePair<string | number>(["a", 1]);
>homogeneousPair : [string, string] | [number, number]
>makePair<string | number>(["a", 1]) : [string, string] | [number, number]
>makePair : <T>(value: [T & not boolean, T & not boolean]) => FilteredPair<T>
>["a", 1] : [string, number]
>"a" : "a"
>1 : 1
type Omitted<T> = "a" extends T ? never : "b";
>Omitted : Omitted<T>
type DependentFilter<T> = T extends Omitted<T> ? never : T;
>DependentFilter : DependentFilter<T>
function dependentFilter<T>(value: T & not Omitted<T>): DependentFilter<T> {
>dependentFilter : <T>(value: T & not Omitted<T>) => DependentFilter<T>
>value : T & not Omitted<T>
// For T = "a" | "b", Omitted<T> is never, so value can be "b", but DependentFilter<T> is "a".
return value;
>value : T & not Omitted<T>
}
const onlyA: "a" = dependentFilter<"a" | "b">("b");
>onlyA : "a"
>dependentFilter<"a" | "b">("b") : "a"
>dependentFilter : <T>(value: T & not Omitted<T>) => DependentFilter<T>
>"b" : "b"
type Filter<T, U> = T extends U ? never : T;
>Filter : Filter<T, U>
function unfiltered<T, U>(value: T): Filter<T, U> {
>unfiltered : <T, U>(value: T) => Filter<T, U>
>value : T
// value can still belong to U, whose members are excluded from Filter<T, U>.
return value;
>value : T
}
type FilteredKeys<T> = {
>FilteredKeys : FilteredKeys<T>
[K in keyof T as K extends "b" ? never : K]: T[K]
};
function excludedKey<T>(value: keyof T & not "c"): keyof FilteredKeys<T> {
>excludedKey : <T>(value: keyof T & not "c") => keyof FilteredKeys<T>
>value : keyof T & not "c"
// Excluding "c" does not prevent value from being the filtered-out key "b".
return value;
>value : keyof T & not "c"
}
function unrelatedKey<T>(value: string & not "b"): keyof FilteredKeys<T> {
>unrelatedKey : <T>(value: string & not "b") => keyof FilteredKeys<T>
>value : string & not "b"
// value is not constrained to keyof T and may name a property T does not have.
return value;
>value : string & not "b"
}
type RemappedKeys<T> = {
>RemappedKeys : RemappedKeys<T>
[K in keyof T as ("a" | "b") extends K ? K : never]: T[K]
};
function remappedKey<T extends { a: unknown; b: unknown }>(
>remappedKey : <T extends { a: unknown; b: unknown; }>(value: keyof T & not "c") => keyof RemappedKeys<T>
>a : unknown
>b : unknown
value: keyof T & not "c"
>value : keyof T & not "c"
): keyof RemappedKeys<T> {
// For T with just "a" and "b", neither key accepts their union, so no keys survive.
return value;
>value : keyof T & not "c"
}
type DependentKeys<T> = {
>DependentKeys : DependentKeys<T>
[K in keyof T as K extends Omitted<K> ? never : K]: T[K]
};
function dependentKey<T>(value: keyof T & not Omitted<keyof T>): keyof DependentKeys<T> {
>dependentKey : <T>(value: keyof T & not Omitted<keyof T>) => keyof DependentKeys<T>
>value : keyof T & not Omitted<keyof T>
// For T = { a: number; b: number }, value can be "b", but only "a" survives the per-key filter.
return value;
>value : keyof T & not Omitted<keyof T>
}
type Repeated<T extends string> = T extends "skip" ? never : `${T}-${T}`;
>Repeated : Repeated<T>
function repeated<T extends string>(value: `${T & not "skip"}-${T & not "skip"}`): Repeated<T> {
>repeated : <T extends string>(value: `${T & not "skip"}-${T & not "skip"}`) => Repeated<T>
>value : `${T & not "skip"}-${T & not "skip"}`
// value can mix different union members in its two T positions, unlike Repeated<T>.
return value;
>value : `${T & not "skip"}-${T & not "skip"}`
}
const same: "a-a" | "b-b" = repeated<"a" | "b">("a-b");
>same : "a-a" | "b-b"
>repeated<"a" | "b">("a-b") : "a-a" | "b-b"
>repeated : <T extends string>(value: `${T & not "skip"}-${T & not "skip"}`) => Repeated<T>
>"a-b" : "a-b"
type Correlated<T> = T extends string ? never : T & { value: T };
>Correlated : Correlated<T>
>value : T
function correlated<T>(value: T & not string & { value: T & not string }): Correlated<T> {
>correlated : <T>(value: T & not string & { value: T & not string; }) => Correlated<T>
>value : T & not string & { value: T & not string; }
>value : T & not string
// The outer T and the value property's T can come from different union members, unlike Correlated<T>.
return value;
>value : T & not string & { value: T & not string; }
}
function trueFunction<T>(value: () => (string & { value: T })): FilteredFunction<string & { value: T }> {
>trueFunction : <T>(value: () => (string & { value: T; })) => FilteredFunction<string & { value: T; }>
>value : () => (string & { value: T; })
>value : T
>value : T
// The check type always extends string, so the result is never, not a function.
return value;
>value : () => (string & { value: T; })
}
function unionFunction<T>(value: () => ([T] | string)): FilteredFunction<[T] | string> {
>unionFunction : <T>(value: () => ([T] | string)) => FilteredFunction<[T] | string>
>value : () => ([T] | string)
// The string union member is filtered out, so the returned function must return [T], not string.
return value;
>value : () => ([T] | string)
}
@@ -38,3 +38,99 @@ const t1: T1 = ['b', 'c'];
type S1<T> = { [K in keyof T & string as K extends '_key' ? never : K]: 1 }[LocalExclude<keyof T & string, '_key'>];
type S2<T> = { [K in string & keyof T as K extends '_key' ? never : K]: 1 }[LocalExclude<string & keyof T, '_key'>];
type ConditionalExclude<T, U> = T extends U ? never : T;
function exclude<T, U>(value: T & not U): ConditionalExclude<T, U> {
return value;
}
type Identity<T> = T;
type AliasedExclude<T, U> = T extends U ? never : Identity<T>;
function aliasedExclude<T, U>(value: T & not U): AliasedExclude<T, U> {
return value;
}
function mappedExclude<T, U>(value: keyof T & not U): keyof ExcludeKey<T, U> {
return value;
}
type AliasedExcludeKey<T, U> = {
[K in keyof T as ConditionalExclude<K, U>]: T[K]
};
function aliasedMappedExclude<T, U>(value: keyof T & not U): keyof AliasedExcludeKey<T, U> {
return value;
}
type Prefixed<T extends string> = T extends "skip" ? never : `prefix${T}`;
function prefixed<T extends string>(value: `prefix${T & not "skip"}`): Prefixed<T> {
return value;
}
type WithSuffix<T extends string, S extends string> = T extends "skip" ? never : `prefix${T}:${S}`;
function withSuffix<T extends string, S extends string>(value: `prefix${T & not "skip"}:${S}`): WithSuffix<T, S> {
return value;
}
type Cased<T extends string> = Uppercase<`prefix${Lowercase<T>}`>;
type FilteredCased<T extends string> = T extends "skip" ? never : Cased<T>;
function cased<T extends string>(value: Cased<T & not "skip">): FilteredCased<T> {
return value;
}
type Alternatives<T extends string> = T extends "skip" ? never : `left${T}` | `right${Uppercase<T>}`;
function alternatives<T extends string>(
value: `left${T & not "skip"}` | `right${Uppercase<T & not "skip">}`
): Alternatives<T> {
return value;
}
type Branded<T> = T extends "skip" ? never : T & { brand: true };
function branded<T>(value: T & not "skip" & { brand: true }): Branded<T> {
return value;
}
interface Values {
a: 1;
b: 2;
skip: 3;
}
type Lookup<T extends keyof Values> = T extends "skip" ? never : Values[T];
function lookup<T extends keyof Values>(value: Values[T & not "skip"]): Lookup<T> {
return value;
}
type PrefixedKeys<T> = {
[K in keyof T & string as K extends "_key" ? never : `prefix${K}`]: T[K]
};
function prefixedKey<T>(value: `prefix${keyof T & string & not "_key"}`): keyof PrefixedKeys<T> {
return value;
}
type FunctionResult<T> = T extends string ? never : () => T;
function falseFunction<T>(value: () => [T]): FunctionResult<[T]> {
return value;
}
type TupleResult<T> = T extends string ? never : [T, T];
function falseTuple<T>(value: [[T], [T]]): TupleResult<[T]> {
return value;
}
type Nested<T extends string> = T extends "skip" ? never : ConditionalExclude<`prefix${T}`, "prefixomit">;
function nested<T extends string>(value: ConditionalExclude<`prefix${T & not "skip"}`, "prefixomit">): Nested<T> {
return value;
}
@@ -0,0 +1,96 @@
// @strict: true
type FilteredFunction<T> = T extends string ? never : () => T;
function makeFunction<T>(): FilteredFunction<T> {
// When T is string, FilteredFunction<T> is never, but this still returns a function.
return () => { throw new Error(); };
}
const impossibleFunction: never = makeFunction<string>();
type FilteredPair<T> = T extends boolean ? never : [T, T];
function makePair<T>(value: [T & not boolean, T & not boolean]): FilteredPair<T> {
// The tuple elements can come from different union members, unlike the distributed FilteredPair<T>.
return value;
}
const homogeneousPair: [string, string] | [number, number] = makePair<string | number>(["a", 1]);
type Omitted<T> = "a" extends T ? never : "b";
type DependentFilter<T> = T extends Omitted<T> ? never : T;
function dependentFilter<T>(value: T & not Omitted<T>): DependentFilter<T> {
// For T = "a" | "b", Omitted<T> is never, so value can be "b", but DependentFilter<T> is "a".
return value;
}
const onlyA: "a" = dependentFilter<"a" | "b">("b");
type Filter<T, U> = T extends U ? never : T;
function unfiltered<T, U>(value: T): Filter<T, U> {
// value can still belong to U, whose members are excluded from Filter<T, U>.
return value;
}
type FilteredKeys<T> = {
[K in keyof T as K extends "b" ? never : K]: T[K]
};
function excludedKey<T>(value: keyof T & not "c"): keyof FilteredKeys<T> {
// Excluding "c" does not prevent value from being the filtered-out key "b".
return value;
}
function unrelatedKey<T>(value: string & not "b"): keyof FilteredKeys<T> {
// value is not constrained to keyof T and may name a property T does not have.
return value;
}
type RemappedKeys<T> = {
[K in keyof T as ("a" | "b") extends K ? K : never]: T[K]
};
function remappedKey<T extends { a: unknown; b: unknown }>(
value: keyof T & not "c"
): keyof RemappedKeys<T> {
// For T with just "a" and "b", neither key accepts their union, so no keys survive.
return value;
}
type DependentKeys<T> = {
[K in keyof T as K extends Omitted<K> ? never : K]: T[K]
};
function dependentKey<T>(value: keyof T & not Omitted<keyof T>): keyof DependentKeys<T> {
// For T = { a: number; b: number }, value can be "b", but only "a" survives the per-key filter.
return value;
}
type Repeated<T extends string> = T extends "skip" ? never : `${T}-${T}`;
function repeated<T extends string>(value: `${T & not "skip"}-${T & not "skip"}`): Repeated<T> {
// value can mix different union members in its two T positions, unlike Repeated<T>.
return value;
}
const same: "a-a" | "b-b" = repeated<"a" | "b">("a-b");
type Correlated<T> = T extends string ? never : T & { value: T };
function correlated<T>(value: T & not string & { value: T & not string }): Correlated<T> {
// The outer T and the value property's T can come from different union members, unlike Correlated<T>.
return value;
}
function trueFunction<T>(value: () => (string & { value: T })): FilteredFunction<string & { value: T }> {
// The check type always extends string, so the result is never, not a function.
return value;
}
function unionFunction<T>(value: () => ([T] | string)): FilteredFunction<[T] | string> {
// The string union member is filtered out, so the returned function must return [T], not string.
return value;
}