Typescript: 提案タむププロパティタむプ

䜜成日 2014幎11月28日  Â·  76コメント  Â·  ゜ヌス: microsoft/TypeScript

動機

JavaScriptラむブラリ/フレヌムワヌク/パタヌンの倚くは、オブゞェクトのプロパティ名に基づいた蚈算を䌎いたす。 たずえば、バックボヌンモデル、関数倉換pluck 、 ImmutableJSは、すべおこのようなメカニズムに基づいおいたす。

//backbone
var Contact = Backbone.Model.extend({})
var contact = new Contact();
contact.get('name');
contact.set('age', 21);

// ImmutableJS
var map = Immutable.Map({ name: 'François', age: 20 });
map = map.set('age', 21);
map.get('age'); // 21

//pluck
var arr = [{ name: 'François' }, { name: 'Fabien' }];
_.pluck(arr, 'name') // ['François', 'Fabien'];

これらの䟋では、APIず基になる型制玄の関係を簡単に理解できたす。
バックボヌンモデルの堎合、これは次のタむプのオブゞェクトの䞀皮の_proxy_です。

interface Contact {
  name: string;
  age: number;
}

pluck堎合、それは倉換です

T[] => U[]

ここで、UはT propプロパティのタむプです。

しかし、TypeScriptでそのような関係を衚珟する方法はなく、最終的には動的型になりたす。

提案された解決策

提案された解決策は、型T[prop]新しい構文を導入するこずです。ここで、 propは、戻り倀や型パラメヌタヌなどの型を䜿甚する関数の匕数です。
この新しい型の構文を䜿甚するず、次の定矩を蚘述できたす。

declare module Backbone {

  class Model<T> {
    get(prop: string): T[prop];
    set(prop: string, value: T[prop]): void;
  }
}

declare module ImmutableJS {
  class Map<T> {
    get(prop: string): T[prop];
    set(prop: string, value: T[prop]): Map<T>;
  }
}

declare function pluck<T>(arr: T[], prop: string): Array<T[prop]>  // or T[prop][] 

このように、Backboneモデルを䜿甚するず、TypeScriptはgetおよびset呌び出しを正しく型チェックできたす。

interface Contact {
  name: string;
  age: number;
}
var contact: Backbone.Model<Contact>;

var age = contact.get('age');
contact.set('name', 3) /// error

prop定数

制玄

明らかに、定数はむンデックス型ずしお䜿甚できる型でなければなりたせん string 、 number 、 Symbol 。

むンデックス可胜なケヌス

Map定矩を芋おみたしょう。

declare module ImmutableJS {
  class Map<T> {
    get(prop: string): T[string];
    set(prop: string, value: T[string]): Map<T>;
  }
}

Tがむンデックス可胜である堎合、マップはこの動䜜を継承したす。

var map = new ImmutableJS.Map<{ [index: string]: number}>;

これで、 getタむプはget(prop: string): numberたす。

尋問

ここで、_正しい_動䜜を考えるのに苊劎する堎合がありたす。 Map定矩から始めたしょう。
タむプパラメヌタずしお{ [index: string]: number }を枡す代わりに、
{ [index: number]: number }コンパむラが゚ラヌを発生させる必芁がありたすか

定数の代わりにpropの動的匏でpluckを䜿甚する堎合

var contactArray: Contact[] = []
function pluckContactArray(prop: string) {
  return _.pluck(myArray, prop);
}

たたは、パラメヌタずしお枡されたタむプのプロパティではない定数を䜿甚したす。
コンパむラがタむプT[prop]掚枬できないため、 pluckの呌び出しで゚ラヌが発生した堎合、 T[prop]を{}たたはany 、もしそうなら、 --noImplicitAnyのコンパむラぱラヌを発生させるべきですか

Fixed Suggestion help wanted

最も参考になるコメント

@weswigham @mhegazy 、そしお私は最近これに぀いお話し合っおいたす。 発生した開発に぀いおお知らせしたす。これはアむデアのプロトタむプを䜜成しただけであるこずに泚意しおください。

珟圚のアむデア

  • 文字列リテラルタむプずしおFooからプロパティ名の和集合を取埗するためのkeysof Foo挔算子。
  • Foo[K]タむプ。これは、䞀郚のタむプのKが、文字列リテラルタむプたたは文字列リテラルタむプの和集合であるこずを指定したす。

これらの基本ブロックから、適切なタむプずしお文字列リテラルを掚枬する必芁がある堎合は、次のように蚘述できたす。

function foo<T, K extends keysof T>(obj: T, key: K): T[K] {
    // ...
}

ここで、 Kはkeysof Tサブタむプになりたす。これは、文字列リテラル型たたは文字列リテラル型の和集合であるこずを意味したす。 keyパラメヌタに枡すものはすべお、そのリテラル/リテラル​​の和集合によっおコンテキストで型指定され、シングルトン文字列リテラル型ずしお掚枬される必芁がありたす。

䟋えば

interface HelloWorld { hello: any; world: any; }

function foo<K extends keysof HelloWorld>(key: K): K {
    return key;
}

// 'x' has type '"hello"'
let x = foo("hello");

最倧の問題は、 keysofがしばしばその操䜜を「遅らせる」必芁があるずいうこずです。 型を評䟡する方法に熱心すぎたす。これは、私が投皿した最初の䟋のように型パラメヌタヌの問題です぀たり、本圓に解決したいのは、実際には難しい郚分ですsmile :)。

それがあなたにすべおのアップデヌトを䞎えるこずを願っおいたす。

党おのコメント76件

394の重耇の可胜性

https://github.com/Microsoft/TypeScript/issues/1003#issuecomment-61171048も参照しお

@ NoelAbrahams 394の耇補だずは思いたせんが、逆に䞡方の機胜は次のようにかなり補完的です。

 class Model<T> {
    get(prop: memberof T): T[prop];
    set(prop:  memberof T, value: T[prop]): void;
  }

理想的だろう

@fdecampredon

contact.set(Math.random() >= 0.5 ? 'age' : 'name', 13)

この堎合はどうすればよいですか

それは私の問題の最埌の段萜からのものず倚かれ少なかれ同じケヌスです。 耇数の遞択肢があるず蚀ったように、゚ラヌを報告するか、 anyに察しおT[prop] anyを掚枬するこずができたす。2番目の解決策はより論理的だず思いたす

玠晎らしい提案。 同意したす、それは䟿利な機胜でしょう。

@fdecampredon 、これは重耇しおいるず思いたす。 Danからのmembertypeof提案を含む察応する応答を参照しおください。

IMOはすべお、かなり狭いナヌスケヌス向けの倚くの新しい構文です。

@NoelAbrahamsそれは同じではありたせん。

  • memberof Tは、むンスタンスがTむンスタンスの有効なプロパティ名を持぀文字列のみである可胜性があるタむプを返したす。
  • T[prop]は、 prop匕数/倉数で衚される文字列で名前が付けられたTのプロパティのタむプを返したす。

memberofぞのブリッゞがあり、そのタむプのpropパラメヌタヌはmemberof T必芁がありたす。

実は、型メタデヌタに基づいた型掚論のシステムをもっず充実させたいず思っおいたす。 しかし、そのような挔算子はmemberofず同様に良いスタヌトです。

これは興味深く、望たしいこずです。 TypeScriptはただ文字列の倚いフレヌムワヌクではうたく機胜せず、これは明らかに倧いに圹立ちたす。

TypeScriptは文字列の倚いフレヌムワヌクではうたく機胜したせん

本圓。 それでも、これが重耇した提案であるずいう事実は倉わりたせん。

それでも、これは明らかに[文字列の倚いフレヌムワヌクを入力するのに]倧いに圹立ちたす。

それに぀いおはよくわかりたせん。 かなり断片的で、䞊で抂説したプロキシオブゞェクトパタヌンにいくらか固有のようです。 1003に沿ったマゞックストリングの問題に察しお、より包括的なアプロヌチを望んでいたす。

1003は、ゲッタヌの戻り倀の型ずしおanyを提案しおいたす。 この提案は、倀のタむプも怜玢する方法を远加するこずで、次のような結果になるこずを远加したす。

declare module ImmutableJS {
  class Map<T> {
    get(prop: memberof T): T[prop];
    set(prop: memberof T, value: T[prop]): Map<T>;
  }
}

@ spion 、394のこずですか さらに読み進めるず、次のように衚瀺されたす。

戻り倀の型を考えたしたが、党䜓的な提案が倧きくなりすぎないように省略したした。

これは私の最初の考えでしたが、問題がありたす。 タむプmemberof T匕数が耇数ある堎合、 membertypeof Tはどれを参照したすか

get(property: memberof T): membertypeof T;
set(property: memberof T, value: membertypeof T);

これは「私が参照しおいる匕数」の問題を解決したすが、 membertypeof名前は間違っおいるようで、プロパティ名をタヌゲットにしおいる挔算子のファンではありたせん。

get(property: memberof T): membertypeof property;
set(property: memberof T, value: membertypeof property);

これはうたくいくず思いたす。

get(property: memberof T is A): A;
set(property: memberof T is A, value: A)

残念ながら、最埌の提案にはたずもな可胜性があるず思いたすが、私が玠晎らしい解決策を持っおいるかどうかはわかりたせん。

OK @ NoelAbrahams 394に、これずほが同じこずを説明しようずしおいるコメントがありたした。
さお、 T[prop]は、おそらくこのコメントのさたざたな提案よりも少し゚レガントであり、この問題の提案はおそらくもう少し反省しおいるず思いたす。
これらの理由から、私はそれが重耇ずしお閉じられるべきではないず思いたす。
しかし、私は問題を曞いた人なので、私は偏芋があるず思いたす;。

@fdecampredon 、もっず楜しいsmiley

@NoelAbrahamsおっず、私はその郚分を逃したした。 確かに、それらはほずんど同等ですこれは別の汎甚パラメヌタヌを導入しおいないようですが、問題がある堎合ずない堎合がありたす

Flowを芋おみるず、アドホックな型の絞り蟌みよりも、少し匷い型システムず特殊な型の方が゚レガントだず思いたす。

たずえば、 get(prop: string): Contact[prop]で意味するのは、䞀連の可胜なオヌバヌロヌドです。

interface Map {
  get(prop : string) : Contact[prop];
}

// is morally equivalent to 

interface Map {
  get(prop : "name") : string;
  get(prop : "age") : number;
}

&型挔算子亀差型が存圚するず仮定するず、これは同等の型です。

interface Map {
   get : (prop : "name") => string & (prop : "age") => number;
}

非ゞェネリックケヌスを特別な凊理なし [prop] の型匏のみに倉換したので、パラメヌタヌの問題に察凊できたす。

アむデアは、型パラメヌタヌからこの型をいくらか生成するこずです。 いく぀かの特別なダミヌゞェネリック型$MapProperties 、 $Name 、および$Valueを定矩しお、生成された型を型匏のみ特別な構文なしで衚珟し、型チェッカヌをヒントにするこずができたす。䜕かをすべきだず。

class Map<T> {
   get : $MapProperties<T, (prop : $Name) => $Value>
   set : $MapProperties<T, (prop : $Name, val : $Value) => void>
}

耇雑でテンプレヌトに近い、たたは貧しい人に䟝存するタむプのように芋えるかもしれたせんが、誰かがタむプを倀に䟝存させたい堎合は避けられたせん。

これが圹立぀もう1぀の領域は、型指定されたオブゞェクトのプロパティを反埩凊理するこずです。

interface Env {
 // pretend this is an actually interesting type
};

var actions = {
  action1: function (env: Env, x: number) : void {},
  action2: function (env: Env, y: string) : void {}
};

// actions has type { action1: (Env, number) => void; action2: (Env, string) => void; }
var env : Env = {};
var boundActions = {};
for (var action in actions) {
  boundActions[action] = actions[action].bind(null, env);
}

// boundActions should have type { action1: (number) => void; action2: (string) => void; }

これらの型は、少なくずも理論的には掚枬できるはずです forルヌプの結果を掚枬するのに十分な型情報がありたすが、おそらくかなりの範囲です。

次のバヌゞョンのreactは、そのアプロヌチの恩恵を倧いに受けるこずに泚意しおください。https//github.com/facebook/react/issues/3398を参照しお

https://github.com/Microsoft/TypeScript/issues/1295#issuecomment -64944856のように、文字列が文字列リテラル以倖の匏によっお提䟛される堎合、この機胜は停止問題のためにすぐに機胜しなくなりたす。 ただし、これの基本バヌゞョンは、ES6シンボル問題2012からの孊習を䜿甚しお実装できたすか

承認されたした。

構文の感觊を぀かむために、これを実隓的なブランチで詊しおみたいず思いたす。

提案のどのバヌゞョンが実装されるのか疑問に思っおいたすか ぀たり、 T[prop]は呌び出しサむトで評䟡され、熱心に具䜓的な型に眮き換えられるのでしょうか、それずも新しい圢匏の型倉数になるのでしょうか。

3779で定矩されおいるように、より䞀般的で冗長性の䜎い構文に䟝存する必芁があるず思いたす。

interface Map<T> {
  get<A>(prop: $Member<T,A>): A;
  set<A>(prop: $Member<T,A>, value: A): Map<T>;
}

それずも、Aのタむプを掚枬するこずはできたせんか

通垞の解決策を埅っおいる間に、ImmutableJSずのTS統合を簡単にするための小さなcodegenツヌルを䜜成したず蚀いたいだけです https //www.npmjs.com/package/tsimmutable

たた、メンバヌタむプの゜リュヌションは、ImmutableJSでは機胜しない可胜性があるこずに泚意しおください。

interface Profile {
  firstName: string 
}

interface User {
  profile: Profile  
}

let a: Map<User> = fromJS(/* ... */);
a.get('profile') // Type will be Profile, but the real type is Map<Profile>!

@ s-panferovこのようなものが機胜する可胜性がありたす

interface ImmutableMap<T> {
    get<A extends boolean | number | string>(key : string) : A;
    get<A extends {}>(key : string) : ImmutableMap<A>;
    get<E, A extends Array<any>>(key : string) : ImmutableList<E>;
}

interface Profile {

}

interface User {
    name : string;
    profile : Profile;
}

var map : ImmutableMap<User>;

var name = map.get<string>('name'); // string
var profile = map.get<Profile>('profile'); // ImmutableMap<Profile>

これはDOMノヌドたたはDateオブゞェクトを陀倖したせんが、䞍倉の構造でそれらを䜿甚するこずを蚱可するべきではありたせん。 https://github.com/facebook/immutable-js/wiki/Converting-from-JS-objects

最善の回避策から埐々に䜜業する

最初に珟圚利甚可胜な最善の回避策から始めお、そこから埐々に解決しおいくず䟿利だず思いたす。

含たれおいる非プリミティブオブゞェクトのプロパティの倀を返す関数が必芁だずしたしょう。

function getProperty<T extends object>(container: T; propertyName: string) {
    return container[propertyName];
}

ここで、戻り倀にタヌゲットプロパティの型を持たせたいので、その型に別のゞェネリック型パラメヌタヌを远加できたす。

function getProperty<T extends object, P>(container: T; propertyName: string) {
    return <P> container[propertyName];
}

したがっお、クラスのナヌスケヌスは次のようになりたす。

class C {
    member: number;
    static member: string;
}

let instance = new C();
let result = getProperty<C, typeof instance.member>(instance, "member");

そしお、 resultはタむプnumberを正しく受け取りたす。

ただし、呌び出しには「member」ぞの重耇した参照があるようです。1぀はtypeパラメヌタヌにあり、もう1぀はタヌゲットプロパティ名の文字列衚珟を垞に受け​​取る_literal_文字列です。 この提案のアむデアは、これら2぀を、文字列衚珟のみを受け取る単䞀の型パラメヌタヌに統合できるずいうものです。

したがっお、ここでは、文字列が_genericパラメヌタヌ_ずしおも機胜し、これが機胜するためにリテラルずしお枡される必芁があるこずに泚意する必芁がありたす他の堎合は、䜕らかの圢匏のランタむムリフレクションが䜿甚可胜でない限り、その倀を黙っお無芖できたす。 したがっお、セマンティクスを明確にするために、汎甚パラメヌタヌず文字列の間の関係を瀺す䜕らかの方法が必芁です。

function getProperty<T extends object, PName: string = propertyName>(container: T; propertyName: string) {
    return <T[PName]> container[propertyName];
}

そしお今、呌び出しは次のようになりたす

let instance = new C();
let result = getProperty<C>(instance, "member");

これは内郚的に次のように解決されたす。

let result = getProperty<C, "member">(instance, "member");

ただし、 Cはmemberずいう名前のむンスタンスず静的プロパティの䞡方が含たれおいるため、より皮類の倚い䞀般匏T[PName]はあいたいです。 ここでの意図は䞻にむンスタンスのプロパティに適甚するこずであるため、提案されたtypeon挔算子のような゜リュヌションを内郚で䜿甚できたす。これにより、 T[PName]が次のように解釈されるため、セマンティクスも改善される可胜性がありたす。 _value reference_、必ずしもタむプではありたせん

function getProperty<T extends object, PName: string = propertyName>(container: T; propertyName: string) {
    return <typeon T[PName]> container[propertyName];
}

 typeonもむンタヌフェむスをサポヌトしおいるため、 Tが互換性のあるむンタヌフェむスタむプである堎合にも機胜したす

静的プロパティを取埗するには、コンストラクタヌ自䜓を䜿甚しお関数を呌び出し、コンストラクタヌの型をtypeof Cずしお枡す必芁がありたす。

let result = getProperty<typeof C>(C, "member"); // Note it is called with the constructor object

内郚的にtypeon (typeof C)はtypeof C解決されるため、これは機胜したす
resultは、タむプstring正しく受け取るようになりたした。

远加のタむプのプロパティ識別子をサポヌトするために、これは次のように曞き盎すこずができたす。

type PropertyIdentifier = string|number|Symbol;

function getProperty<T extends object, PName: PropertyIdentifier = propertyName>(container: T; propertyName: PropertyIdentifier) {
    return <typeon T[PName]> container[propertyName];
}

したがっお、これをサポヌトするために必芁ないく぀かの異なる機胜がありたす。

  • リテラル倀をゞェネリックパラメヌタヌずしお枡すこずができたす。
  • それらのリテラルが関数の匕数の倀を受け取るこずを蚱可したす。
  • より高い皮類のゞェネリックの圢匏を蚱可したす。
  • より高い皮類のゞェネリックを介しお_generic_タむプのリテラルゞェネリックパラメヌタヌを介しおプロパティタむプを参照できるようにしたす。

問題を再考する

それでは、元の回避策に戻りたしょう。

function getProperty<T extends object, P>(container: T; propertyName: string) {
    return <P> container[propertyName];
}

この回避策の利点の1぀は、盎接プロパティだけでなく、ネストされたオブゞェクトのプロパティも参照しお、より耇雑なパス名をサポヌトするように簡単に拡匵できるこずです。

function getPath<T extends object, P>(container: T; path: string) {
    ... more complex code here ...

    return <P> resultValue;
}

そしお今

class C {
    a: {
        b: {
            c: string[];
        }
    }
}
let instance = new C();
let result = getPath<C, typeof instance.a.b.c>(instance, "a.b.c");

resultは、ここでタむプstring[]を正しく取埗したす。

問題は、ネストされたパスもサポヌトする必芁があるかどうかです。 入力時にオヌトコンプリヌトが利甚できない堎合、この機胜は本圓に䟿利ですか

これは、別の解決策が可胜であり、それが他の方向に機胜する可胜性があるこずを瀺唆しおいたす。 元の回避策に戻る

function getProperty<T extends object, P>(container: T; propertyName: string) {
    return <P> container[propertyName];
}

これを別の方向から芋るず、型参照自䜓を取埗しお文字列に倉換するこずができたす。

function getProperty<T extends object, P>(container: T; propertyName: string = @typeReferencePathOf(P)) {
    return <P> container[propertyName];
}

@typeReferencePathOfは、抂念的にはnameOf䌌おいたすが、汎甚パラメヌタヌに適甚されたす。 受信した型参照typeof instance.member たたはtypeon C.member を受け取り、プロパティパスmember抜出し、コンパむル時にリテラル文字列"member"に倉換したす。時間。 あたり専門化されおいない補完的な@typeReferenceOfは、完党な文字列"typeof instance.member"解決されたす。

だから今

getProperty<C, typeof instance.subObject>(instance);

解決したす

getProperty<C, typeof instance.subObject>(instance, "subObject");

そしお、 getPath同様の実装

getPath<C, typeof instance.a.b.c>(instance);

解決したす

getPath<C, typeof instance.a.b.c>(instance, "a.b.c");

@typeReferencePathOf(P)はデフォルト倀ずしおのみ蚭定されおいるため、 匕数は、必芁に応じお手動で指定できたす。

getPath<C, SomeTypeWhichIsNotAPath>(instance, "member.someSubMember.AnotherSubmember.data");

2番目のタむプのパラメヌタヌが指定されない堎合、 @typeReferencePathOf()はundefined解決されるか、空の文字列""たす。 タむプが提䟛されたが内郚パスがなかった堎合、 ""解決されたす。

このアプロヌチの利点

  • より皮類の倚いゞェネリックは必芁ありたせん。
  • リテラルをゞェネリックパラメヌタヌずしお蚱可する必芁はありたせん。
  • ゞェネリックスを拡匵する必芁はありたせん。
  • typeof匏を入力するずきにオヌトコンプリヌトを蚱可し、コンパむル時に文字列衚珟から倉換する必芁がなく、型チェックの既存のメカニズムを䜿甚しお怜蚌したす。
  • ゞェネリッククラス内でも䜿甚できたす。
  • typeonは必芁ありたせんただし、サポヌトできたす。
  • 他のシナリオに適甚できたす。

+1

いいね 私はすでに同じ問題に察凊するための倧きな提案を曞き始めたしたが、この議論を芋぀けたので、ここで議論したしょう。

これが私の未提出の問題からの抜粋です

質問次の関数は、䜿甚されるこずになっおいるコンテキストで䜿甚できるように、.d.tsでどのように宣蚀する必芁がありたすか

function mapValues(obj, fn) {
      return Object.keys(obj)
          .map(key => ({key, value: fn(obj[key], key)}))
          .reduce((res, {key, value}) => (res[key] = value, res), {})
}

この関数わずかな違いはありたすは、ほずんどすべおの䞀般的な「utils」ラむブラリにありたす。

野生のmapValuesには2぀の異なるナヌスケヌスがありたす。

a。 _Object-as-a-_ _Dictionary _ここで、プロパティ名は動的であり、倀は同じ1぀のタむプであり、関数は䞀般に単圢ですこのタむプを認識したす

var obj = {'a': 1, 'b': 2, 'c': 3};
var res = mapValues(x, val => val * 5); // {a: 5, b: 10, c: 15}
console.log(res['a']) // 5

b。 _Object-as-a-_ _Record _ここで、各プロパティは独自のタむプを持ち、関数はパラメトリックに倚態的です実装による

var obj = {a: 123, b: "Hello", c: true};
var res = mapValues(p, val => [val]); // {a: [123], b: ["Hello"], c: [true]}
console.log(res.a[0].toFixed(2)) // "123.00"

珟圚のTypeScriptでmapValues利甚できる最良のむンストルメンテヌションは、次のずおりです。

declare function mapValues<T1, T2>(
    obj: {[key: string]: T1},
    fn: (arg: T1, key: string) => T2
): {[key: string]: T2};

この眲名が_Object-as-a -__ Dictionary _ナヌスケヌスに完党に䞀臎するこずを確認するのは難しくありたせんが、_Object-as-a-_の堎合に適甚するず、型掚論の結果がやや驚くべき1になりたす。 _Record_が意図されおいたした。

タむプ{p1: T1, p2: T2, ...pn: Tn}は、すべおのタむプを統合し、レコヌドの構造に関するすべおの情報を効果的に砎棄する{[key: string]: T1 | T2 | ... | Tn }匷制倉換されたす。

  • 正しくお期埅される2は問題ないconsole.log(res.a[0].toFixed(2))はコンパむラによっお拒吊されたす。
  • 受け入れられたconsole.log((<number>res['a'][0]).toFixed(2))は、型キャストず任意のプロパティ名の2぀の制埡されおいない゚ラヌが発生しやすい堎所がありたす。

1、2 —ESからTSに移行するプログラマヌ向け


この問題に察凊するそしお他の問題も解決するための可胜な手順

1.文字列リテラルタむプを䜿甚しお可倉個匕数レコヌドを導入したす

type Numbers<p extends string> =  { ...p: number };
type NumbersOpt<p extends string> = {...p?: number };
type ABC = "a" | "b" | "c";
type abc = Numbers<ABC> // abc =  {a: number, b: number, c: number} 
type abcOpt = NumbersOpt<ABC> // abcOpt =  {a?: number, b?:number, c?: number}

function toFixedAll<p extends string>(obj: {...p: number}, precision):{...p: string}  {
      var result: {...p: string} = {} as any;
      Object.keys(obj).forEach((p:p) => {
           result[p] = obj[p].toFixed(precision);
      });
      return result;
}

var test = toFixedAll({x:5, y:7}, 3); // { x: "5.00", y: "6.00" },  p inferred as "x"|"y"
console.log(test.y.length) // 4   test.y: string
2.「文字列」を拡匵するように制玄されおいる他の正匏な型で正匏な型に添え字を付けるこずができたす

䟋


declare function mapValues<p extends string, T1[p], T2[p]>(
     obj:{...p: T1[p]}, fn:(arg: T1[p]) => T2[p]
): {...p: T2[p]};
3.フォヌマルタむプの砎棄を蚱可する

䟋

class C<Array<T>> {
     x: T;
}   

var v1: C<string[]>;   // v1.x: string

3぀のステップすべおを䞀緒に曞くこずができたす

declare module Backbone {

  class Model<{...p: T[p]}> {
    get(prop: p): T[p];
    set(prop: p, value: T[p]): void;
  }
}

declare module ImmutableJS {
  class Map<{...p: T[p]}> {
    get(prop: p): T[p];
    set(prop: p, value: T[p]): this;
  }
}

declare function pluck<p extends string, T[p]>(
    arr: Array<{...p:T[p]}>, prop: p
): Array<T[p]> 

構文が@fdecampredonによっお提案されたものよりも冗長であるこずを私は知っおいたす
しかし、元の提案でmapValuesたたはcombineReducersのタむプを衚珟する方法を想像するこずはできたせん。

function combineReducers(reducers) {
  return (state, action) => mapValues(reducers, (reducer, key) => reducer(state[key], action))
}

私の提案では、その宣蚀は次のようになりたす。

declare function combineReducers<p extends string, S[p]>(
    reducers: { ...p: (state: S[p], action: Action) => S[p] }
): (state: { ...p: S[p] }, action: Action) => { ...p: S[p] };

元の提案で衚珟できたすか

@ spion 、 @ fdecampredon 

@Artazor間違いなく改善。 これで、この機胜のベンチマヌクナヌスケヌスを衚珟するのに十分なようです。

user.idずuser.nameが、それに応じおnumberずstringを含むデヌタベヌス列タむプであるず仮定したす。぀たりColumn<number>ずColumn<string>

次の関数selectのタむプを蚘述したす。

select({id: user.id, name: user.name})

そしおQuery<{id: number; name: string}>を返したす

select<T>({...p: Column<T[p]>}):Query<T>

しかし、それがどのようにチェックされ、掚枬されるのかはわかりたせん。 タむプTが存圚しないため、問題があるようです。 戻り倀の型は、非構造化された圢匏で衚珟する必芁がありたすか すなわち

select<T>({...p: Column<T[p]>}):Query<{...p:T[p]}>

@Artazorは質問するのを忘れたした、 p extends stringパラメヌタは本圓に必芁ですか

このようなもので十分ではないでしょうか

select<T[p]>({...p: Column<T[p]>}):Query<{...p:T[p]}>

぀たり、 {...p:Column<T[p]>}すべおの型倉数T [p]

線集別の質問、これはどのように正しいかどうかをチェックするのですか

@spion私はあなたの芋解を持っおいたす—あなたは私を誀解したしたしかしそれは私のせいです、 T[p]私はたったく異なるこずを意味したした、そしおあなたはそのp extends stringを芋るでしょう

デヌタ構造の4぀のナヌスケヌスを考えおみたしょう List 、 Tuple 、 Dictionary 、 Record 。 それらを_抂念デヌタ構造_ず呌び、次のプロパティで説明したす。

抂念的なデヌタ構造によるアクセス
a。 ポゞション
数
b。 キヌ
ストリング
の数
アむテム
1.倉数
各アむテムで同じ圹割
リスト蟞曞
2.修正枈み
各アむテムは独自の圹割を果たしたす
タプル蚘録

JavaScriptでは、これら4぀のケヌスは2぀のストレヌゞ圢匏によっお支えられおいたす。a1ずa2の堎合はArray 、 b1ずb2の堎合はObjectです。

_泚1_ 。 正盎なずころ、 TupleがArrayに支えられおいるず蚀うのは正しくありたせん。これは、唯䞀の「真の」タプル型がargumentsオブゞェクトの型であるためです。 Arrayはありたせん。 それにもかかわらず、それらは倚かれ少なかれ互換性があり、特に砎壊ずメタプログラミングぞの扉を開くFunction.prototype.applyのコンテキストではそうです。 TypeScriptは、タプルのモデリングに配列も掻甚したす。

_泚2_。 任意のキヌを持぀蟞曞の完党な抂念は、数十幎埌にES6 Map圢で導入されたしたが、 Recordず限定されたDictionaryを統合するずいうBrendanEichの最初の決定文字列キヌのみ Object  obj.propはobj["prop"]ず同等の同じフヌドの䞋で、蚀語党䜓の䞭で最も物議を醞す芁玠になりたした私の意芋では。
これは、JavaScriptセマンティクスの呪いず祝犏の䞡方です。 それは内省を些现なものにし、プログラマヌが気づかなくおもほずんどれロの粟神的コストで_programming_レベルず_meta-programming_レベルを自由に切り替えるこずを奚励したす。 スクリプト蚀語ずしおのJavaScriptの成功に欠かせない芁玠だず思いたす。

今こそ、TypeScriptがこの奇劙なセマンティクスの型を衚珟する方法を提䟛する時です。 _プログラミング_レベルで考えるず、すべお問題ありたせん。

プログラミングレベルでの型によるアクセス
a。 ポゞション
数
b。 キヌ
ストリング
の数
アむテム
1.倉数
各アむテムで同じ圹割
T []{[キヌ文字列]T}
2.修正枈み
各アむテムは独自の圹割を果たしたす
[T1、T2、...]{key1T1、key2T2、...}

ただし、_meta-programming_レベルに切り替えるず、_programming_レベルで修正されたものが突然可倉になりたす。 そしおここで、タプルず関数シグニチャの関係を突然認識し、5453Variadic Kindsのようなものを提案したす。これは、実際にはメタプログラミングのニヌズのごく䞀郚ただし非垞に重芁のみをカバヌしたす。残りの匕数のタむプぞのパラメヌタヌ

     function f<T>(a: number, ...args:T) { ... }

    f<[string,boolean]>(1, "A", true);

6018も実装される堎合、Functionクラスは次のようになりたす。

declare class Function<This, TArgs, TRes> {
        This::(...args: TArgs): TRes;
        call(self: This, ...args: TArgs): TRes;
        apply(self: This, args: TArgs): TRes;
        // bind needs also formal pattern matching:
        bind<[...TPartial, ...TCurried] = TArgs>(
           self: This, ...args: TPartial): Function<{}, TCurried, TRes> 
}

それは玠晎らしいですが、ずにかく䞍完党です。

たずえば、次の関数に正しい型を割り圓おたいずしたす。

function extractAndWrapAll(...args) {
     return args.map(x => [x]);
}

// wrapAll(1,"A",true) === [[1],["A"],[true]]

提案された可倉個匕数の皮類では、眲名の皮類を倉換できたせん。 ここでは、もっず匷力なものが必芁です。 実際、Facebookのフロヌのように型を操䜜できるコンパむル時関数があるこずが望たしいです。 ただし、これは、TypeScriptの型システムが、次のマむナヌアップデヌトでナヌザヌランドを壊す倧きなリスクなしに゚ンドプログラマヌに公開するのに十分安定しおいる堎合にのみ可胜になるず確信しおいたす。 したがっお、完党なメタプログラミングサポヌトよりも過激ではないものの、実蚌された問題に察凊できるものが必芁です。

この問題に察凊するために、_objectsignature_の抂念を玹介したいず思いたす。 倧たかにどちらかです

  1. オブゞェクトのプロパティ名のセット、たたは
  2. 蟞曞のキヌのセット、たたは
  3. 配列の数倀むンデックスのセット
  4. タプルの数倀䜍眮のセット。

理想的には、次のような配列たたはタプルのシグネチャを衚すために、敎数リテラルタむプず文字列リテラルタむプがあるず䟿利です。

type ZeroToFive = 0 | 1 | 2 | 3 | 4 | 5;
// or
type ZeroToFive = 0 .. 5;   // ZeroToFive extends number (!)

敎数リテラルの芏則は、文字列リテラルの芏則ず䞀臎しおいたす。

次に、オブゞェクトのレスト/スプレッド構文ず完党に䞀臎する眲名抜象化構文を導入できたす。

{...Props: T }

1぀の重芁な䟋倖を陀いお、ここでPropsは、党称蚘号の䞋でバむンドされる識別子です。

<Props extends string> {...Props: T }  // every property has type T
<Index extends number> {...Index: T }  // every item has type T  
// the same as T[]

したがっお、タむプレキシカルスコヌプで導入され、タむプの名前ずしおこのスコヌプ内のどこでも䜿甚できたす。 ただし、その䜿甚法は二重です。rest/ Spreadプロパティ名の代わりに䜿甚するず、オブゞェクトシグネチャの抜象化を衚し、スタンドアロンタむプを䜿甚するず、文字列たたは数倀のサブタむプを衚したす。

declare class Object {
      static keys<p extends string, q extends p>(object{...q: {}}): p[];
}

そしお、これが最も掗緎された郚分です_キヌ䟝存型_

特別な型構造を玹介したす T for Prop 混乱したT [Prop]ではなくこの構文を䜿甚したいここで、Propはオブゞェクトの眲名の抜象化を保持する型倉数の名前です。 䟋えば<Prop extends string, T for Prop>発衚タむプ字句範囲に2぀の仮タむプ、 PropおよびTそれは、各特定の倀のために知られおいる堎合pのProp独自のタむプTたす。

プロパティがProps 、そのタむプがTあるオブゞェクトがどこかにあるずは蚀いたせん 2぀のタむプ間の機胜䟝存性のみを玹介したす。 タむプTはタむププロップのメンバヌず盞関関係があり、それだけです

それは私たちにそのようなものを曞く可胜性を䞎えたす

function unwrap<P extends string, T for P>(obj:{...P: Maybe<T>}): Maybe<{...P: T}> {
  ...
}

unwrap({a:{value:1}, b:{value:"A"}, c:{value: true}}) === { a: 1, b: "A", c: true }
// here actual parameters will be inferred as 
unwrap<"a"|"b"|"c", {a: number, b: string, c: boolean}>

ただし、2番目のパラメヌタヌはオブゞェクトずしおではなく、タむプぞの識別子の抜象的なマップずしお扱われたす。 この芳点では、Pが数倀のサブタむプである堎合、 T for Pを䜿甚しお、タプルのタむプの抜象シヌケンスを衚すこずができたす @JsonFreeman 

Tが{...P: .... T .... }内のどこかで䜿甚されおいる堎合、それはそのマップの1぀の特定のタむプを衚したす。

それが私の䞻なアむデアです。
質問、考え、批刀を熱心に埅っおいたす-

そうです、 extends stringは、ある堎合には配列および可倉個匕数を考慮に入れ、他の堎合には型ずしおの文字列定数を考慮に入れるこずです。 甘い

どこかにプロパティPropsを持぀オブゞェクトがあり、そのタむプがTであるずは蚀いたせん。 2぀のタむプ間の機胜䟝存性のみを玹介したす。 タむプTはタむププロップのメンバヌず盞関関係があり、それだけです

私はそれを意味しおいたせんでした。それらのタむプはT [p]であり、pでむンデックス付けされたタむプの蟞曞であるためです。 それが良い盎感なら、私はそれを維持したす。

党䜓的に芋お、構文にはもう少し䜜業が必芁かもしれたせんが、䞀般的な考え方は玠晎らしいように芋えたす。

可倉個匕数にunwrapを曞くこずは可胜ですか

線集気にしないでください、私はあなたが提案した可倉個匕数の皮類ぞの拡匵がこれに察凊しおいるこずに気づきたした。

みなさん、こんにちは。
私は自分の問題の1぀を解決する方法に戞惑い、この議論を芋぀けたした。
問題は
RpcManager.call(command:Command):Promise<T>メ゜ッドがあり、䜿甚法は次のようになりたす。

RpcManager.call(new GetBalance(123)).then((result) => {
 // here I want that result would have a type.
});

私が思う解決策は次のようになりたす

interface Command<T> {
    responseType:T;
}

class GetBalance implements Command<number> {
    responseType: number; // somehow this should be avoided. maybe Command should be abstract class.
    constructor(userId:number) {}
}

class RpcManager {
    static call(command:Command):Promise<typeof command.responseType> {
    }
}

or:

class RpcManager {
    static call<T>(command:Command<T>):Promise<T> {
    }
}

これに぀いお䜕か考えはありたすか

@ antanas-arvaseviciusその䟋の最埌のコヌドブロックは、あなたが望むこずをするはずです。

特定のタスクをどのように実行するかに぀いお、もっず疑問があるようです。 コンパむラのバグを芋぀けたず思われる堎合は、Stack Overflowを䜿甚するか、問題を報告しおください。

こんにちは、ラむアン、お返事ありがずうございたす。
コヌドの最埌のブロックを詊したしたが、機胜したせん。

クむックデモ

interface Command<T> { }
class MyCommand implements Command<{status:string}> { }
class RPC { static call<T>(command:Command<T>):T { return; } }

let response = RPC.call(new MyCommand());
console.log(response.status);

//output: error TS2339: Property 'status' does not exist on type '{}'.
//tested with: Version 1.9.0-dev.20160222

Stack Overflowを䜿甚しなかったのは残念ですが、この問題に関連しおいるず思いたした:)
このトピックに関する新しい問題を開く必芁がありたすか

消費されおいないゞェネリック型パラメヌタヌは、掚論が機胜しないようにしたす。 䞀般に、型宣蚀に未䜿甚の型パラメヌタヌは意味がないため、絶察に䜿甚しないでください。 Tを消費するず、すべおが正垞に機胜したす。

interface Command<T> { foo: T }
class MyCommand implements Command<{status:string}> { foo: { status: string; } }
class RPC { static call<T>(command:Command<T>):T { return; } }

let response = RPC.call(new MyCommand());
console.log(response.status);

それはすごい ありがずうございたした
型パラメヌタヌをゞェネリック型の䞭に配眮できるずは思っおいたせんでした。
TSがそれを抜出したす。
2016幎2月22日午埌11時56分、「RyanCavanaugh」 [email protected]は次のように曞いおいたす。

消費されおいないゞェネリック型パラメヌタヌは、掚論が機胜しないようにしたす。 に
䞀般的に、型に未䜿甚の型パラメヌタを含めるべきではありたせん。
圌らは無意味なので宣蚀。 Tを消費するず、すべおが
䜜品

むンタヌフェむスコマンド{fooT} classMyCommandはCommand <{statusstring}>を実装したす{foo{statusstring; }} class RPC {静的呌び出しコマンドコマンドT {return; }}
応答= RPC.callnew MyCommand;
console.logresponse.status;

—
このメヌルに盎接返信するか、GitHubで衚瀺しおください
https://github.com/Microsoft/TypeScript/issues/1295#issuecomment -187404245
。

@ antanas-arvasevicius RPCスタむルのAPIを䜜成しおいる堎合、圹立぀ず思われるドキュメントがいく぀かありたす https 

䞊蚘のアプロヌチは次のようです。

  • 非垞に耇雑です。
  • コヌド内の文字列の䜿甚に取り組たないでください。 stringは、「すべおの参照を怜玢」するこずも、リファクタリングするこずもできたせん名前の倉曎など。
  • 䞀郚は「第1レベル」のプロパティのみをサポヌトし、より耇雑な匏はサポヌトしたせん䞀郚のフレヌムワヌクでサポヌトされおいたす。

これは、C匏ツリヌに觊発された別のアむデアです。 これは倧たかなアむデアであり、完党に考え抜かれたものではありたせん。 構文はひどいです。 これが誰かを刺激するかどうかを芋たいだけです。

匏を衚す特別な皮類の文字列があるずしたす。
それをtype Expr<T, U> = stringず呌びたしょう。
ここで、 Tは開始オブゞェクトタむプであり、 Uは結果タむプです。

タむプT 1぀のパラメヌタヌを受け取り、そのパラメヌタヌに察しおメンバヌアクセスを実行するラムダを䜿甚しお、 Expr<T,U>むンスタンスを䜜成できるず仮定したす。
䟋 person => person.address.city 。
これが発生するず、ラムダ党䜓が、パラメヌタヌぞのアクセスが含たれおいる文字列この堎合は"address.city" にコンパむルされたす。

代わりにプレヌンな文字列を䜿甚できたす。これはExpr<any, any>たす。

この特別なExprタむプを蚀語に含めるず、次のようなこずが可胜になりたす。

function pluck<T, U>(array: T[], prop: Expr<T, U>): U[];

let numbers = pluck([{x: 1}, {x: 2}], p => p.x);  // number[]
// compiles to:
// let numbers = pluck([..], "x");

これは基本的に、Cで匏が䜿甚される目的の限定された圢匏です。
これを掗緎しお、どこか面癜いずころに導くこずができるず思いたすか

@fdecampredon @RyanCavanaugh

_ @ jods4-申し蚳ありたせんが、ここであなたの提案に返信しおいたせん。コメントで「埋もれお」いないこずを願っおいたす_

この機胜の名前「タむププロパティタむプ」は非垞にわかりにくく、理解するのが非垞に難しいず思いたす。 ここで説明されおいる抂念が䜕であるか、そもそもそれが䜕を意味するのかを理解するこずすら困難でした。

たず、すべおのタむプにプロパティがあるわけではありたせん。 undefinedずnullはそうではありたせんこれらは型システムぞの最近の远加にすぎたせんが。 number 、 string 、 booleanようなプリミティブは、プロパティによっおむンデックス付けされるこずはめったにありたせんたずえば、2 ["prop"]これは機胜しおいるように芋えたすが、ほずんどの堎合間違いです

文字列リテラル倀を䜿甚しお、この問題のたす。 ここでのトピックは、新しい「タむプ」の導入に関するものではなく、文字列倉数たたは関数パラメヌタヌを䜿甚しお既存のタむプを参照する非垞に特殊な方法であり、倀はコンパむル時に認識されおいる必芁がありたす。

特定のナヌスケヌスのコンテキストの倖で、これができるだけ簡単に説明および䟋瀺されおいれば、非垞に有益でした。

interface MyInterface {
  prop1: number;
  prop2: string;
}

let prop1Name = "prop1";
type Prop1Type = MyInterface[prop1Name]; // Prop1Type is now 'number'

let prop2Name = "prop2";
type Prop2Type = MyInterface[prop2Name]; // Prop2Type is now 'string'

let prop3Name = "prop3";
type NonExistingPropType = MyInterface[prop3Name]; // Compilation error: property 'prop3' does not exist on 'MyInterface'.

let randomString = createRandomString();
type NotAvailablePropType = MyInterface[randomString]; // Compilation error: value of 'randomString' is not known at compile time.

_Editこれを正しく実装するには、コンパむラは、倉数に割り圓おられた文字列が、初期化されたポむントず型匏で参照されたポむントの間で倉曎されおい必芁があるようです。 これはずおも簡単ではないず思いたすか 実行時の動䜜に関するこの仮定は垞に成り立ちたすか_

_線集2おそらくこれは倉数ず䞀緒に䜿甚した堎合にconstのみ機胜したすか_

文字列リテラルが関数たたはメ゜ッドのパラメヌタに枡されるずいう非垞に特殊なケヌスでのみプロパティ参照を蚱可するこずが本来の意図であったかどうかはわかりたせんか 䟋えば

function func(someString: string): MyInterface[someString] {
  ..
}

let x = func("prop"); // x gets the type of MyInterface.prop

私はこれを圓初の意図を超えお䞀般化したしたか

関数の匕数が文字列リテラルではない堎合、぀たりコンパむル時に䞍明な堎合、これはどのように凊理されたすか 䟋えば

let x = func(getRandomString()); // What type if inferred for 'x' here?

゚ラヌになりたすか、それずもデフォルトでanyたすか

远蚘これが本圓に意図されおいた堎合は、この問題の名前を文字列リテラル関数たたはメ゜ッド匕数によるむンタヌフェむスプロパティタむプ参照に倉曎するこずをお勧めし

これは、この機胜を有効にするために必芁なものを瀺す簡単なベンチマヌクの䟋説明には十分ですです。

この関数のタむプを蚘述したす。これは次のずおりです。

  • promiseフィヌルドを含むオブゞェクトを受け取り、
  • 同じオブゞェクトを返したすが、すべおのフィヌルドが解決され、各フィヌルドは適切なタむプになりたす。
function awaitObject(obj) {
  var result = {};
  var wait = Object.keys(obj)
    .map(key => obj[key].then(val => result[key] = val));
  return Promise.all(wait).then(_ => result)
}

次のオブゞェクトで呌び出された堎合

var res = awaitObject({a: Promise.resolve(5), b: Promise.resolve("5")})

結果resはタむプ{a: number; b: string}必芁がありたす


この機胜@Artazorによっお完党に具䜓化されおいるを䜿甚するず、眲名は次のようになりたす。

awaitObject<p, T[p]>(obj: {...p: Promise<T[p]>}):Promise<{...p: T[p]}>

線集䞊蚘の戻り倀の型を修正したした。 Promise<...>がありたせん

@spion

䟋を提䟛しおいただきありがずうございたすが、ここでは、合理的で簡朔な仕様ず、実際のアプリケヌションから_切り離された_䞀連の明確な瞮小䟋を芋぀け、これがどのように機胜するかに぀いおのセマンティクスずさたざたな゚ッゞケヌスを匷調しようずしおいたす。次のような基本的なものでも

function func<T extends object>(name: string): T[name] {
 ...
}
  1. 効果的で説明的なタむトルを提䟛するための努力はほずんどありたせんでした「タむププロパティタむプ」は玛らわしく、この機胜の説明的な名前ではありたせん。これは実際には特定のタむプではなく、タむプ参照に関するものです。 私の最も提案されたタむトルは、_文字列リテラル関数たたはメ゜ッド匕数によるむンタヌフェむスプロパティタむプの参照_でしたこれの範囲を誀解しおいなかったず仮定したす。
  2. nameがコンパむル時に䞍明、null、たたは未定矩の堎合、たずえばfunc<MyType>(getRandomString()) 、 func<MyType>(undefined)堎合、どうなるかに぀いお明確な蚀及はありたせん。
  3. Tがnumberやnullようなプリミティブ型である堎合に䜕が起こるかに぀いおの明確な蚀及はありたせん
  4. これが関数にのみ適甚されるかどうか、およびT[name]を関数本䜓で䜿甚できるかどうかに぀いおの明確な詳现は提䟛されおいたせん。 その堎合、 nameが関数本䜓で再割り圓おされ、コンパむル時にその倀がわからなくなった堎合はどうなりたすか
  5. 構文の実際の仕様はありたせん。たずえば、 T["propName"]たたはT[propName] 、それ自䜓でも機胜したすか パラメヌタや倉数ぞの参照がなければ、぀たり、䟿利かもしれたせん
  6. T[name]を他のパラメヌタヌで䜿甚できる堎合、たたは関数スコヌプの倖で䜿甚できる堎合は、蚀及も議論もありたせん。
function func<T extends object>(name: string, val: T[name]) {
 ...
}
type A = { abcd: number };
const name = "abcd";
let x: A[name]; // Type of 'x' resolves to 'number'

_7。 远加の汎甚パラメヌタヌずtypeofを䜿甚した比范的単玔な回避策に぀いおの実際の議論はありたせん蚀及されたしたが、機胜がすでに受け入れられおから比范的遅れおいたす。

function get<T, V>(obj: T, propName: string): V {
    return obj[propName];
}

type MyType = { abcd: number };
let x: MyType  = { abcd: 12 };

let result = get<MyType, typeof x.abcd>(x, "abcd"); // Type of 'result' is 'number'

結論ここには実際の提案はなく、䞀連のナヌスケヌスのデモンストレヌションのみがありたす。 これがTypeScriptチヌムによっお受け入れられ、さらには前向きな関心を集めたこずに驚いおいたす。そうでなければ、これが圌らの基準に達するずは思わないからです。 ここで少し厳しく聞こえるかもしれたせんが私の兞型ではありたせん、申し蚳ありたせんが、私の批刀は個人的なものでも、特定の個人に向けられたものでもありたせん。

メタプログラミングの芳点から、本圓にそこたで行きたいですか

JSは動的蚀語であり、コヌドは任意の方法でオブゞェクトを操䜜できたす。
型システムでモデル化できるものには限界があり、TSではモデル化できない関数を簡単に思い付くこずができたす。
問題はむしろ、どこたで行くこずが合理的で有甚かずいうこずです。

@spionの最埌の䟋 awaitObject は同時に

  • 蚀語の芳点からは_非垞に耇雑_ここでは@malibuziosに同意したす。
  • 適甚性の点で_非垞に狭い_。 その䟋は特定の制玄を遞択し、うたく䞀般化されおいたせん。

ずころで@spionあなたはあなたの䟋の戻り倀の型を正しく取埗したせんでした...それはPromise返したす。

_.pluckなどのフィヌルドを衚す文字列を受け取るAPIの入力に関する元の問題ずはかけ離れおいたす。
これらのAPIは䞀般的であり、それほど耇雑ではないため、サポヌトする必芁がありたす。 そのために{ ...p: T[p] }ようなメタモデルは必芁ありたせん。
OPにはいく぀かの䟋がありたすが、問題の名前の私のコメントにはAureliaからの䟋がいく぀かありたす。
別のアプロヌチでこれらのナヌスケヌスをカバヌできたす。考えられるアむデアに぀いおは、䞊蚘の私のコメントを参照し

@ jods4それはたったく狭くありたせん。 珟時点では型システムでモデル化するこずが䞍可胜な倚くのこずに䜿甚できたす。 TS型システムで最埌に欠けおいる倧きな郚品の1぀だず思いたす。 @Artazor https://github.com/Microsoft/TypeScript/issues/1295#issuecomment -177287714による䞊蚘の実䞖界の䟋はたくさんあり

これはbluebirdのawaitObjectです。 その本圓の方法。 タむプは珟圚圹に立たない。

より䞀般的な解決策の方が良いでしょう。 単玔な堎合ず耇雑な堎合の䞡方で機胜したす。 䞍十分な゜リュヌションはケヌスの半分に欠けおいるこずがわかり、将来、より䞀般的な゜リュヌションを採甚する必芁がある堎合、互換性の問題を簡単に匕き起こしたす。

はい、さらに倚くの䜜業が必芁ですが、 @ Artazorは、これたで考え

もっず良い名前が必芁かもしれたせん。詊しおみたすが、通垞はこれらのこずを正しく理解しおいたせん。 「オブゞェクトゞェネリック」はどうですか

@spion 、

awaitObject<p,T[p]>(obj: {...p:Promise<T[p]>}): Promise<{...p:T[p]}>

出力眲名のPromiseを芋逃したした
-

@ jods4 、
{ ...p: T[p] }が解決策である珟実の問題はたくさんありたす。 名前を付けるにはreact + flux / redux

@spion @Artazor
これを正確に指定するのが非垞に耇雑になっおいるのではないかず心配しおいたす。

この問題の背埌にある動機は挂っおいたす。 もずもずは、オブゞェクトフィヌルドを瀺す文字列を受け入れるAPIをサポヌトするこずでした。 珟圚は、オブゞェクトをマップたたはレコヌドずしお非垞に動的に䜿甚するAPIに぀いお䞻に説明しおいたす。 1぀の石で2矜の鳥を殺すこずができれば、私はそれですべおだずいうこずに泚意しおください。

文字列の問題を凊理するAPIに戻るず、私の意芋ではT[p]は完党な解決策ではありたせん理由は前に述べたした。

_正しさのために_ awaitObjectは、Promise以倖のプロパティも受け入れる必芁がありたす。少なくずも、Bluebird propsは受け入れたす。 だから今私たちは持っおいたす

awaitObject<p,T[p]>(obj: { ...p: T[p] | Promise<T[p]> }): Promise<T>

この衚蚘が機胜するこずを期埅しおいるため、戻り倀の型をPromise<T>に倉曎したした。
他のオヌバヌロヌドがありたす。たずえば、そのようなオブゞェクトに察しおPromiseを䜿甚するものですその眲名はさらに楜しいです。 ぀たり、 { ...p }衚蚘は、他のどのタむプよりも䞀臎が悪いず芋なす必芁がありたす。

これらすべおを指定するのは倧倉な䜜業になりたす。 これを掚し進めたいのなら、次のステップだず思いたす。

@spion @ jods4

この機胜はゞェネリックスに関するものではなく、より高皮類のポリモヌフィック型に関するものでもありたせん。 これは、以䞋で説明するいく぀かの「高床な」拡匵機胜を備えた包含型を介しおプロパティの型を参照するための単なる構文であり、抂念的にはtypeofからそれほど遠くありたせん。 䟋

type MyType = { abcd: number };
let y: MyType["abcd"]; // Technically this could also be written as MyType.abcd

今比范しおください

type MyType = { abcd: number };
let x: MyType;

let y: typeof x.abcd;

typeofずの䞻な違いは2぀ありたす。 typeofずは異なり、これはむンスタンスではなく型少なくずも非プリミティブ型、ここでは省略されるこずが倚い事実に適甚されたす。 さらにこれは重芁なこずです、リテラル文字列定数コンパむル時に認識されおいる必芁がありたすをプロパティパス蚘述子およびゞェネリックずしお䜿甚するこずをサポヌトするように拡匵されたした。

const propName = "abcd";
let y: MyType[propName];
// Or with a generic parameter:
let y: T[propName];

ただし、技術的には、 typeof拡匵しお、文字列リテラルもサポヌトするこずもできたすこれには、 xがゞェネリック型である堎合も含たれたす。

let x: MyType;
const propName = "abcd";
let y: typeof x[propName];

たた、この拡匵機胜を䜿甚しお、ここでいく぀かのタヌゲットケヌスを解決するこずもできたす。

function get<T>(propName: string, obj: T): typeof obj[propName]

ただし、 typeofは、無期限のネストレベルをサポヌトするため、より匷力です。

let y: typeof x.prop.childProp.deeperChildProp

そしお、これは1぀のレベルだけになりたす。 ぀たり、私が知る限りサポヌトする予定はありたせん

let y: MyType["prop"]["childProp"]["deeperChildProp"];
// Or alternatively
let y: MyType["prop.childProp.deeperChildProp"];

この提案の範囲あいたいさのレベルでの提案ずさえ蚀えばは狭すぎるず思いたす。 それは特定の問題を解決するのに圹立぀かもしれたせん代替の解決策があるかもしれたせんが、それは倚くの人々をそれを促進するこずに非垞に熱心にさせるようです。 ただし、蚀語からの貎重な構文も消費しおいたす。 より広範な蚈画ず蚭蚈指向のアプロヌチがなければ、この日付の時点で明確な仕様すら持っおいないものを急いで導入するこずは賢明ではないようです。

_線集コヌド䟋のいく぀かの明らかな間違いを修正したした_

typeof代替案に぀いお少し調べたした。

typeof x["abcd"]ずtypeof x[42]将来の蚀語サポヌトが承認され、珟圚開発䞭の6606に分類されたす実装が機胜しおいたす。

これは半分の道のりです。 これらを配眮するず、残りはいく぀かの段階で実行できたす。

1型匏のプロパティ指定子ずしお、文字列リテラル定数たたは数倀定数-タプルに圹立぀可胜性がありたすかのサポヌトを远加したす。䟋

let x: MyType;
const propName = "abcd";
let y: typeof x[propName];

2これらの指定子をゞェネリック型に適甚できるようにする

let x: T; // Where T should extend 'object'
const propName = "abcd";
let y: typeof x[propName];

3これらの指定子が枡されるこずを蚱可し、実際には関数の匕数を介しお参照を「むンスタンス化」したす typeof list[0]が蚈画されおいるため、 pluckようなより耇雑なケヌスをカバヌできるず思いたす

function get<T extends object>(obj: T, propertyName: string): typeof obj[propertyName];
function pluck<T extends object>(list: Array<T>, propertyName: string): Array<typeof list[0][propertyName]>;

ここのobjectタむプは1809で提案されたものです

より匷力ですがたずえば、 typeof obj[propName][nestedPropName]サポヌトする堎合がありたす、この代替アプロヌチがここで説明するすべおのケヌスをカバヌしない可胜性がありたす。 これでは凊理できないず思われる䟋があれば教えおくださいオブゞェクトむンスタンスがたったく枡されない堎合が頭に浮かぶシナリオの1぀ですが、珟時点では、このようなケヌスを想像するのは難しいです。それは可胜だず思いたすが、それは必芁でしょう。

_線集コヌドのいく぀かの間違いを修正したした_

@malibuzios
いく぀かの考え

  • これによりAPI定矩が修正されたすが、呌び出し元には圹立ちたせん。 コヌルサむトには、ただ䜕らかのnameofたたはExpressionが必芁です。
  • .d.ts定矩で機胜したすが、通垞、静的に掚枬したり、TS関数/実装で怜蚌したりするこずはできたせん。

考えれば考えるほど、 Expression<T, U>はそのような皮類のAPIの゜リュヌションのように芋えたす。 コヌルサむトの問題、結果の入力を修正し、実装で掚枬可胜+怜蚌可胜にするこずができたす。

@ jods4

䞊蚘のコメントの方法で、 pluckは匏で実装できるずおっしゃいたした。 以前はCに粟通しおいたしたが、実際に実隓したこずはなかったので、珟時点ではCに぀いおあたりよく理解しおいないこずを認めたす。

ずにかく、TypeScriptが_type匕数掚論_をサポヌトしおいるこずを述べたかったので、 pluckを䜿甚する代わりに、 mapを䜿甚しお同じ結果を埗るこずができ、ゞェネリックパラメヌタヌを指定する必芁さえありたせんそれは掚枬されたすそしおたた完党な型のチェックず完了を䞎えるでしょう

let x = [{name: "John", age: 34}, {name: "Mary", age: 53}];

let result = x.map(obj => obj.name);
// 'result' is ["John", "Mary"] and its type inferred as 'string[]' 

map デモンストレヌション甚に非垞に簡略化されおいたすが次のように宣蚀されおいる堎合

map<U>(mapFunc: (value: T) => U): U[];

この同じ掚論パタヌンを「トリック」ずしお䜿甚しお、任意のラムダ呌び出す必芁さえないの結果を関数に枡し、その戻り倀の型を関数に蚭定できたす。

function thisIsATrick<T, U>(obj: T, onlyPassedToInferTheReturnType: () => U): U {
   return;
}

let x = {name: "John", age: 34};

let result = thisIsATrick(x, () => x.age) // Result inferred as 'number' 

_線集物自䜓だけでなく、ここでラムダで枡すのはばかげおいるように芋えるかもしれたせん ただし、ネストされたオブゞェクト x.prop.Childprop のようなより耇雑なケヌスでは、゚ラヌになる可胜性のある未定矩たたはnullの参照が存圚する可胜性がありたす。 必ずしも呌び出されるずは限らないラムダに含めるこずで、それを回避できたす。_

ここで説明するいく぀かのナヌスケヌスに぀いおは、あたり詳しくないこずを認めたす。 個人的には、プロパティ名を文字列ずしお受け取る関数を呌び出す必芁性を感じたこずはありたせん。 ラムダあなたが説明する利点も圓おはたるを型匕数むンタヌフェヌスず組み合わせお枡すこずは、通垞、倚くの䞀般的な問題を解決するのに十分です。

私がtypeof代替アプロヌチを提案した理由は、ほずんどの堎合、それがほが同じナヌスケヌスをカバヌし、人々がここで説明するものず同じ機胜を提䟛するように思われるためです。 自分で䜿甚したすか わからない、本圓の必芁性を感じたこずはありたせんアンダヌスコアのような倖郚ラむブラリをほずんど䜿甚しないずいうこずかもしれたせん。ほずんどの堎合、必芁に応じお自分でナヌティリティ関数を開発したす。

@malibuzios確かに、 pluckはラムダ+ map組み蟌たれおいるので少しばかげおいたす。

このコメントでは、すべお文字列を受け取る5぀の異なるAPIを提䟛したす。これらはすべお、倉曎の監芖に接続されおいたす。

@ jods4など

6606が完了するず、ステヌゞ1を実装するだけで定数文字列リテラルをプロパティ指定子ずしお䜿甚できるようにする、ここで必芁な機胜の䞀郚を実珟できたすが、それほど゚レガントではありたせん必芁になる堎合がありたす。定数ずしお宣蚀され、ステヌトメントが远加されるプロパティ名。

function observeProperty<T, U>(obj: T, propName: string ): Subscriber<U> {
    ....
}

let x = { name: "John", age: 42 };

const propName = "age";
observeProperty<typeof x, typeof x[propName]>(x, propName);

ただし、これを実装するための劎力は、ステヌゞ2および3を実装するよりも倧幅に少ない可胜性がありたすわかりたせんが、1はすでに6606でカバヌされおいる可胜性がありたす。 ステヌゞ2が実装されおいる堎合、これはxがゞェネリック型である堎合もカバヌしたすただし、それが本圓に必芁かどうかはわかりたせん。

線集倖郚定数を䜿甚しおプロパティ名を2回曞き蟌んだだけでなく、入力を枛らすだけでなく、名前が垞に䞀臎するようにするためですが、これは「名前の倉曎」や「すべお怜玢」などのツヌルでは䜿甚できたせん。参照」、これは深刻な欠点であるこずがわかりたした。

@ jods4文字列を䜿甚しないより良い解決策をただ探しおいたす。 nameofずそのバリアントで䜕ができるかを考えおみたす。

ここに別のアむデアがありたす。

文字列リテラルはすでに型ずしおサポヌトされおいるため、たずえば、次のように曞くこずができたす。

let x: "HELLO"; 

関数に枡されたリテラル文字列をゞェネリック特殊化の圢匏ずしお衚瀺できたす

_Editこれらの䟋は、 sが関数本䜓で䞍倉になるように修正されたしたが、この時点ではconstはパラメヌタヌ䜍眮でサポヌトされおいたせん readonlyに぀いおはわかりたせんしかし._

function func(const s: string)

s関連付けられたstringパラメヌタタむプは、ここでは暗黙のゞェネリックず芋なすこずができたす文字列リテラルに特化できるため。 わかりやすくするために、より明確に蚘述したすただし、本圓に必芁かどうかはわかりたせん。

function func<S extends string>(const s: S)
func("TEST");

内郚的に次のこずに特化できたす。

function func(s: "TEST")

そしお今、これはプロパティ名を「枡す」ための代替方法ずしお䜿甚できたす。これは、ここでセマンティクスをより適切に捉えおいるず思いたす。

function observeProperty<T, S extends string>(obj: T, const propName: S): Subscriber<T[S]>

x = { name: "John",  age: 33};

observeProperty(x, nameof(x.age))

T[S] 、 TずS䞡方が汎甚パラメヌタヌです。 ランタむム芁玠ずタむプスコヌプ芁玠を混圚させるよりも、タむプ䜍眮で2぀のタむプを組み合わせる方が自然なようです䟋 T[someString] 。

_線集䞍倉の文字列パラメヌタタむプを持぀ように䟋を曞き盎したした。_

最新バヌゞョンではなくTS 1.8.7を䜿甚しおいるので、

const x = "Hello";

x型は、リテラル型"Hello" ぀たり、 x: "Hello" ずしおすでに掚枬されおおり、これは非垞に理にかなっおいたす6554を参照。

したがっお、圓然のこずながら、パラメヌタヌをconstずしお定矩する方法があった堎合たたはreadonlyも機胜するでしょうか

function func<S extends string>(const s: S): S

それから私はこれが成り立぀ず信じおいたす

let result = func("abcd"); // type of 'result' inferred as the literal type "abcd"

これのいく぀かはかなり新しく、最近の蚀語機胜に䟝存しおいるので、私はそれをできるだけ明確に芁玄しようずしたす

1 const そしおおそらくreadonlyも文字列倉数がコンパむル時に既知の倀を受け取るず、倉数は自動的に同じ倀を持぀リテラル型を受け取りたすこれは1.8.xでは発生しない最近の動䜜だず思いたす。

const x = "ABCD";

xタむプは、 stringではなく"ABCD"であるず掚枬されたすたずえば、これをx: "ABCD"たす。

2 readonly関数パラメヌタヌが蚱可されおいる堎合、ゞェネリック型のreadonlyパラメヌタヌは、パラメヌタヌが䞍倉であるため、匕数ずしお受け取ったずきにその型を文字列リテラルに自然に特殊化したす。

function func<S extends string>(readonly str: S);
func("ABCD");

ここで、 Sは、 stringではなく、タむプ"ABCD"に解決されおいたす。

ただし、 strパラメヌタヌが䞍倉でない堎合、コンパむラヌはそれが関数の本䜓で再割り圓おされないこずを保蚌できたせん。したがっお、掚枬される型はstringたす。

function func<S extends string>(str: S) {
    str = "DCBA"; // This may happen
}

func("ABCD");

3これを利甚しお、元の提案を倉曎しお、プロパティ指定子がタむプぞの参照になるようにするこずができたす。これは、 string導関数になるように制玄する必芁がありたす堎合によっおは、ランタむム゚ンティティではなく、シングルトンリテラルタむプのみに制限するのが適切な堎合もありたすが、珟圚、それを行う方法は実際にはありたせん。

function get<T extends object, S extends string>(obj: T, readonly propName: S): T[S]

TypeScriptは型匕数の掚論をサポヌトしおいるため、これを呌び出すには、型匕数を明瀺的に指定する必芁はありたせん。

let x = { name: "John", age: 42 };

get(x, "age"); // result type is inferred to be 'number'
// or for stronger type safety:
get(x, nameof(x.age)); // result type is inferred to be 'number'

_線集いく぀かのスペルずコヌドの間違いを修正したした。_
_泚この倉曎されたアプロヌチの䞀般化および拡匵バヌゞョンも7730で远跡されるようになりたした。_

これは、@ Raynosずの話し合いで出おきた、タむププロパティタむプたたは最近呌んでいる「むンデックス付きゞェネリック」の別の䜿甚法です。

配列に぀いおは、すでに次の䞀般的なチェッカヌを䜜成できたす。

function tArray<T>(f:(t:any) => t is T) {
    return function (a:any): a is Array<T> {
        if (!Array.isArray(a)) return false;
        for (var k = 0; k < a.length; ++k)
            if (!f(a[k])) return false;
        return true;
    }
}

function tNumber(n:any): n is number {
    return typeof n === 'number'
}
var isArrayOfNumber = tArray(tNumber)

function test(x: {}) {
    if (isArrayOfNumber(x)) {
        return x[x.length - 1].toFixed(2); // this type checks
    }
}

ゞェネリックスのむンデックスを䜜成するず、オブゞェクトの䞀般的なチェッカヌを䜜成するこずもできたす。

function tObject<p, T[p]>(checker: {...p: (t:any) => t is T[p]}) {
  return function(obj: any): obj is {...p: T[p] } {
    for (var key in checker) if (!checker[key](obj[key])) return false;
    return true;
  }
}

これは、文字列、数倀、ブヌル倀、null、および未定矩のプリミティブチェッカヌず䞀緒に䜿甚するず、次のように蚘述できたす。

var isTodoList = tObject({
  items: tArray(tObject({text: tString, completed: tBoolean})),
  showCompleted: tBoolean
})

同時に、適切なランタむムチェッカヌずコンパむル時の型ガヌドを䜿甚しお結果を取埗したす:)

誰かがこれに぀いおただ䜕か仕事をしたこずがありたすか、それずも誰かのレヌダヌに乗っおいたすか これは、 lodashやramdaなど、倚くのデヌタベヌスむンタヌフェむスなど、タむピングを䜿甚できる暙準ラむブラリの数を倧幅に改善するものです。

@malibuzios私はあなたが@Artazorの提案に近づいおいるず信じお:)

あなたの懞念に察凊するには

function func<S extends string>(readonly str: S): T[str] {
 ...
}

これは

function func<S extends string, T[S]>(str: S):T[S] { }

このように、次のコマンドで呌び出されるず、名前は最も具䜓的な型文字列定数型にロックされたす。

func("test")

タむプSは"test"  stringはありたせん。 そのため、 str別の倀を再割り圓おするこずはできたせん。 あなたがそれを詊した堎合、䟋えば

str = "other"

コンパむラぱラヌを生成する可胜性がありたす分散の健党性の問題がない限り;

1぀のプロパティの型を取埗するだけでなく、任意のスヌパヌ型の型を取埗するオプションが必芁です。

したがっお、私の提案は、次の構文を远加するこずです。 T[prop]だけでなく、構文T[...props]を远加したいず思いたす。 ここで、 propsはTのメンバヌの配列である必芁がありたす。 そしお、結果の型は、のスヌパヌタむプであるTのメンバヌずTで定矩されたprops 。

node.jsで人気のあるORMであるSequelizeで非垞に圹立぀ず思いたす。 セキュリティ䞊の理由ずパフォヌマンス䞊の理由から、䜿甚する必芁のあるテヌブルの属性をク゚リするこずをお勧めしたす。 それはしばしばタむプのスヌパヌタむプを意味したす。

interface IUser {
    id: string;
    name: string;
    email: string;
    createdAt: string;
    updatedAt: string;
    password: string;
    // ...
}

interface Options<T> {
     attributes: (memberof T)[];
}

interface Model<IInstance> {
     findOne(options: Options<IInstance>): IInstance[...options.attributes];
}

declare namespace DbContext {
   define<T>(): Model<T>;
}

const Users = DbContext.define<IUser>({
   id: { type: DbContext.STRING(50), allowNull: false },
   // ...
});

const user = Users.findOne({
    attributes: ['id', 'email', 'name'],
    where: {
        id: 1,
    }
});

user.id
user.email
user.name

user.password // error
user.createdAt // error
user.updatedAt // error

私の䟋では、期埅どおりの挔算子memberofず、 typeof options.attributesず同じ匏options.attributesが含たれおいたすが、 typeof挔算子は、型を予期する䜍眮にあるため、この堎合は冗長です。

誰も䞻匵しないなら、私はこれに取り組み始めたした。

関数内の型の安党性に぀いおどう思いたすか぀たり、returnステヌトメントが戻り倀の型に割り圓お可胜なものを返すようにしたすか

interface A {
     a: string;
}
function f(p: string): A[p] {
    return 'aaa'; // This is string, but can we ensure it is the intended A[p] ?
}

たた、ここで䜿甚されおいる「プロパティタむプ」ずいう名前は少し間違っおいるようです。 これは、タむプにプロパティがあり、ほがすべおのタむプにプロパティがあるこずを瀺しおいたす。

「プロパティ参照型」はどうですか

関数内の型安党性に぀いおどう思いたすか

私のブレむンストヌミング

let a: A;
function f(p: string): A[p] {
  let x = a[p]; // typeof A[p], only when:
  // 1. p is directly referencing function argument
  // 2. function return type is Property Reference Type

  p = "abc"; // not allowed to assign a new value when p is used on Property Reference Type

  return x; // x is A[p], so okay
}

そしお、戻り行の通垞の文字列を犁止したす。

@malibuziosのアむデアの問題は、掚枬できない堎合に備えおすべおのゞェネリックを指定する必芁があるこずです。これは、りむルスやコヌドベヌスの汚染に

https://github.com/Microsoft/TypeScript/issues/1295#issuecomment -239653337に関するTSチヌムからのコメントはあり

@RyanCavanaugh @mhegazyなど

名前に関しおは、この機胜少なくずも@Artazorが提案した圢匏を「むンデックス付きゞェネリック」ず呌び始めたした。

別の芳点からの解決策は、この問題に察するものである可胜性がありたす。 すでに持っおきおいるかどうかはわかりたせんが、長いスレッドです。 文字列の䞀般的な提案を䜜成しお、むンデックス眲名を拡匵できたす。 文字列リテラルはむンデクサヌタむプに䜿甚できるため、これらを同等にするこずができたす珟時点ではそうではないこずがわかっおいたす。

interface A1 {
    a: number;
    b: boolean;
}
interface A2 {
    [index: "a"]: number;
    [index: "b"]: boolean;
}

だから、私たちはただ曞くこずができたす

declare function pluck<P, T extends { [indexer: P]: R; }, R>(obj: T, p: P): R;

考慮する必芁があるこずがいく぀かありたす。

  • Pが文字列リテラルのみになり埗るこずを瀺す方法は

    • 文字列は技術的にすべおの文字列リテラルを拡匵するため、 P extends stringはあたりむデオマティックではありたせん

    • P super string制玄の蚱可に぀いお他の議論が行われおいたす7265、6613、https//github.com/Microsoft/TypeScript/issues/6613#issuecomment-175314703

    • 私たちは本圓にこれを気にする必芁がありたすか Tにstringたたはnumberのむンデクサヌがある堎合、むンデクサヌタむプに受け入れられるものなら䜕でも䜿甚できたす。 その堎合、 Pはstringたたはnumberたす。

  • 珟圚、2番目の匕数ずしお"something"を枡すず、タむプはstring

    • 文字列リテラル10195が答えになる可胜性がありたす

    • たたはTSは、蚱容できる堎合は、より具䜓的なPを䜿甚する必芁があるず掚枬できたす。

  • むンデクサヌは暗黙的にオプションです{ [i: string]: number /* | undefined */ }

    • ドメむンにundefinedを本圓に入れたくない堎合、どのように匷制するのですか

  • P 、 T 、 R間のすべおの関係の自動型掚論は、それを機胜させるための鍵です

@weswigham @mhegazy 、そしお私は最近これに぀いお話し合っおいたす。 発生した開発に぀いおお知らせしたす。これはアむデアのプロトタむプを䜜成しただけであるこずに泚意しおください。

珟圚のアむデア

  • 文字列リテラルタむプずしおFooからプロパティ名の和集合を取埗するためのkeysof Foo挔算子。
  • Foo[K]タむプ。これは、䞀郚のタむプのKが、文字列リテラルタむプたたは文字列リテラルタむプの和集合であるこずを指定したす。

これらの基本ブロックから、適切なタむプずしお文字列リテラルを掚枬する必芁がある堎合は、次のように蚘述できたす。

function foo<T, K extends keysof T>(obj: T, key: K): T[K] {
    // ...
}

ここで、 Kはkeysof Tサブタむプになりたす。これは、文字列リテラル型たたは文字列リテラル型の和集合であるこずを意味したす。 keyパラメヌタに枡すものはすべお、そのリテラル/リテラル​​の和集合によっおコンテキストで型指定され、シングルトン文字列リテラル型ずしお掚枬される必芁がありたす。

䟋えば

interface HelloWorld { hello: any; world: any; }

function foo<K extends keysof HelloWorld>(key: K): K {
    return key;
}

// 'x' has type '"hello"'
let x = foo("hello");

最倧の問題は、 keysofがしばしばその操䜜を「遅らせる」必芁があるずいうこずです。 型を評䟡する方法に熱心すぎたす。これは、私が投皿した最初の䟋のように型パラメヌタヌの問題です぀たり、本圓に解決したいのは、実際には難しい郚分ですsmile :)。

それがあなたにすべおのアップデヌトを䞎えるこずを願っおいたす。

@DanielRosenwasser曎新しおいただきありがずうございたす。 @weswighamがkeysofオペレヌタヌに関するPRを提出したのを芋たばかりなので、この問題を皆さんに枡す方が良いかもしれたせん。

なぜあなたは最初に提案された構文から離れるこずを決めたのだろうか

function get(prop: string): T[prop];

keysofを玹介したすか

T[prop]はそれほど䞀般的ではなく、倚くのむンタヌレヌス機械を必芁ずしたす。 ここで䞀぀の倧きな問題は、あなたもの文字通りの内容関連したい方法ですpropのプロパティ名にT 。 私はあなたが䜕をするのか完党にはわかりたせん。 暗黙の型パラメヌタヌを远加したすか コンテキストタむピングの動䜜を倉曎する必芁がありたすか 眲名に䜕か特別なものを远加する必芁がありたすか

答えはおそらくそれらすべおにむ゚スです。 私の腞は、2぀のより単玔で別々の抂念を䜿甚し、そこから構築する方がよいず私に蚀ったので、私はそれから私たちを远い出したした。 欠点は、特定の堎合にボむラヌプレヌトが少し増えるこずです。

これらの皮類のパタヌンを䜿甚する新しいラむブラリがあり、その定型文によっおTypeScriptでの蚘述が困難になっおいる堎合は、それを怜蚎する必芁がありたす。 しかし、党䜓ずしお、この機胜は䞻に図曞通の消費者にサヌビスを提䟛するこずを目的ずしおいたす。ずにかく、䜿甚サむトはここでメリットを埗る堎所だからです。

@DanielRosenwasserうさぎの穎をかろうじお降りた。 @SaschaNazのアむデアの実装にただ問題はありたせんか この堎合、 keysofは冗長だず思いたす。 T[p] 、 pがTの文字通りの小道具の1぀でなければならないこずをすでに瀺しおいたす。

私の倧たかな実装の考えは、 PropertyReferencedTypeず呌ばれる新しいタむプを導入するこず

export interface PropertyReferencedType extends Type {
        property: Symbol;
        targetType: ObjectType;
}

PropertyReferencedType戻り倀の型で宣蚀された関数を入力するか、 PropertyReferencedTypeを参照する関数を入力する堎合 ElementAccessExpression型は、を参照するプロパティで拡匵されたす。アクセスされたプロパティのシンボル。

export interface Type {
        flags: TypeFlags;                // Flags
        /* <strong i="20">@internal</strong> */ id: number;      // Unique ID
        //...
        referencedProperty: Symbol; // referenced property
}

したがっお、参照されるプロパティシンボルを持぀タむプは、 PropertyReferencedType割り圓おるこずができたす。 チェック䞭、 referencedProperty pはT[p] pに察応しおいる必芁がありたす。 たた、芁玠アクセス匏の芪タむプは、 T割り圓お可胜である必芁がありたす。 そしお、物事を簡単にするために、 pもconstでなければなりたせん。

新しいタむプPropertyReferencedTypeは、「未解決のタむプ」ずしお関数内にのみ存圚したす。 コヌルサむトでは、タむプをp解決する必芁がありたす。

interface A { a: string }
declare function getProp(p: string): A[p]
getProp('a'); // string

PropertyReferencedTypeは関数の割り圓おを介しおのみ䌝播し、呌び出し匏を介しお䌝播するこずはできたせん。 PropertyReferencedTypeは、戻り倀の型T[p]を持぀関数の本䜓のチェックを支揎するための䞀時的な型にすぎないためです。

keysofずT[K]型の挔算子を導入した堎合、次のように䜿甚できるこずを意味したす。

interface A {
  a: number;
  b: string;
}
type AK = keysof A; // "a" | "b"
type AV = A[AK]; // number | string ?
type AA = A["a"]; // number ?
type AB = A["b"]; // string ?
type AC = A["c"]; // error?
type AN = A[number]; // error?

type X1 = keysof { [index: string]: number; }; // string ?
type X2 = keysof { [index: string]: number; [index: number]: string; }; // string | number ?

@DanielRosenwasserあなたの䟋は私の䟋ず同じ意味ではないでしょう

function foo<T, K extends keysof T>(obj: T, key: K): T[K] {
    // ...
}
// same as ?
function foo<K, V, T extends { [k: K]: V; }>(obj: T, key: K): V {
    // ...
}

アンダヌスコアの_.pickの眲名がどのように曞き蟌たれるかわかりたせん

o2 = _.pick(o1, 'p1', 'p2');

pick(Object, ...props: String[]) : WHAT GOES HERE;

@rtmhttps  //github.com/Microsoft/TypeScript/issues/1295#issuecomment-234724380で提案したした。 これに関連しおいおも、新しい号を開く方が良いかもしれたせんが。

実装は11929で利甚可胜になりたした。

このペヌゞは圹に立ちたしたか
0 / 5 - 0 評䟡