提供一个界面(来自无法更改的现有.d.ts文件):
interface Foo {
[key: string]: any;
bar(): void;
}
有没有一种方法可以使用映射类型(或其他方法)来导出没有索引签名的新类型?即它只有方法“ bar():void;”
答案 0 :(得分:10)
使用 TypeScript v4.1 key remapping 导致非常简洁的解决方案。
在其核心,它使用 Mihail's answer 中稍微修改的逻辑:虽然已知键是 string
或 number
的子类型,但后者不是相应文字的子类型。另一方面,string
是所有可能字符串的并集(number
也是如此),因此是自反的(type res = string extends string ? true : false; //true
成立)。
这意味着您可以在每次将 never
或 string
类型分配给键类型时解析为 number
,从而有效地将其过滤掉:
interface Foo {
[key: string]: any;
[key: number]: any;
bar(): void;
}
type RemoveIndex<T> = {
[ P in keyof T as string extends P ? never : number extends P ? never : P ] : T[P]
};
type FooWithOnlyBar = RemoveIndex<Foo>; //{ bar: () => void; }
答案 1 :(得分:6)
有一种方法需要TypeScript 2.8的Conditional Types。
它基于以下事实:'a' extends string
但string
不是extends 'a'
interface Foo {
[key: string]: any;
bar(): void;
}
type KnownKeys<T> = {
[K in keyof T]: string extends K ? never : number extends K ? never : K
} extends { [_ in keyof T]: infer U } ? U : never;
type FooWithOnlyBar = Pick<Foo, KnownKeys<Foo>>;
您可以从中得到通用名称:
// Generic !!!
type RequiredOnly<T extends Record<any,any>> = Pick<T, KnownKeys<T>>;
type FooWithOnlyBar = RequiredOnly<Foo>;
有关为何KnownKeys<T>
确实起作用的解释,请参见以下答案:
答案 2 :(得分:4)
通过 TypeScript 4.4,该语言获得了对更复杂索引签名的支持。
public class JwtUser implements UserDetails {
private String token;
private Long id;
private String username;
private String secondName;
private String password;
private String phone;
private String description;
private String email;
private boolean active;
private Date lastPasswordResetDate;
private Collection<? extends GrantedAuthority> authorities;
public JwtUser(Long id, String username, String secondName, String password, String phone, String description,
String email, boolean active, Date lastPasswordResetDate,
Collection<? extends GrantedAuthority> authorities) {
this.id = id;
this.username = username;
this.secondName = companyName;
this.password = password;
this.phone = phone;
this.description = description;
this.email = email;
this.active = active;
this.lastPasswordResetDate = lastPasswordResetDate;
this.authorities = authorities;
}
public JwtUser(String token, Long id, String username, String secondName, String phone, String description,
String email, Collection<? extends GrantedAuthority> authorities) {
this.token=token;
this.id = id;
this.username = username;
this.secondName = companyName;
this.phone = phone;
this.description = description;
this.email = email;
this.authorities = authorities;
}
public JwtUser(String username, String password, Collection<? extends GrantedAuthority> authorities,
String email, boolean active) {
this.username = username;
this.password=password;
this.authorities = authorities;
this.email = email;
this.active=active;
}
public JwtUser(){
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
@JsonIgnore
public Long getId() {
return id;
}
public String getSecondName() {
return companyName;
}
public String getPhone() {
return phone;
}
public String getDescription() {
return description;
}
public String getEmail() {
return email;
}
public boolean isActive() {
return active;
}
@JsonIgnore
public Date getLastPasswordResetDate() {
return lastPasswordResetDate;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
@JsonIgnore
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username=username;
}
@JsonIgnore
@Override
public boolean isAccountNonExpired() {
return true;
}
@JsonIgnore
@Override
public boolean isAccountNonLocked() {
return true;
}
@JsonIgnore
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
interface FancyIndices {
[x: symbol]: number;
[x: `data-${string}`]: string
}
键可以通过在之前发布的类型中为其添加大小写来轻松捕获,但这种检查方式无法检测到无限模板文字。1
但是,我们可以通过修改检查以查看使用每个键构造的对象是否可分配给空对象来实现相同的目标。这是有效的,因为“真实”键将要求使用 symbol
构造的对象具有属性,因此不可分配,而作为索引签名的键将导致可能仅包含空对象的类型。< /p>
Record<K, 1>
中试用
测试:
type RemoveIndex<T> = {
[K in keyof T as {} extends Record<K, 1> ? never : K]: T[K]
}
1 您可以使用一次处理一个字符的递归类型来检测一些无限模板文字,但这不适用于长键。
答案 3 :(得分:0)
不是。您不能“减去”像这样的界面。每个成员都是公开的,任何声称实现Foo
的人都必须实现它们。通常,您只能通过extends
或声明合并扩展接口,而不能从接口中删除内容。
答案 4 :(得分:0)
没有真正通用的方法,但是如果您知道需要哪些属性,则可以使用Pick:
interface Foo {
[key: string]: any;
bar(): void;
}
type FooWithOnlyBar = Pick<Foo, 'bar'>;
const abc: FooWithOnlyBar = { bar: () => { } }
abc.notexisting = 5; // error