Typescript: 提案:null許容型ではない

作成日 2014年07月22日  ·  358コメント  ·  ソース: microsoft/TypeScript

JSDocに基づく型宣言のための2つの新しい構文を導入します

var myString: !string = 'hello world'; //non-nullable
var myString1: ?string = 'hello world'; // nullable 
var myString2: string = 'hello world'; // nullable 
var myString3 = 'hello world'; // nullable

デフォルトでは、タイプはnull許容です。

2つの新しいコンパイラフラグ:

  • inferNonNullableTypeは、コンパイラにnull許容型を推測させます。
var myString3 = 'hello world' // typeof myString is '!string', non-nullable
  • nonNullableTypeByDefault (もっと良い名前があるかもしれません):
var myString: !string = 'hello world'; // non-nullable
var myString1: string = 'hello world'; // non-nullable 
var myString2: ?string = 'hello world'; // nullable 
var myString3 = 'hello world' // non-nullable
Committed Fixed Suggestion

最も参考になるコメント

クローズされた問題についてコメントした場合は申し訳ありませんが、どこに質問すればよいかわかりません。興味がなければ、これは新しい問題の価値があるとは思いません。
ファイルごとに暗黙のnullを処理することは可能でしょうか?
同様に、noImplicitNullを使用して多数のtdファイルを処理しますが(それらは間違いなく型指定されており、そのように考案されているため)、ソースをimplicitNullとして処理しますか?
誰かがこれが役に立つと思いますか?

全てのコメント358件

文字列以外の型は本質的にnull許容であるため、例として使用することをお勧めします。 :P
「!」のユーザーとコンパイラーから、null許容型ではないことが問題になっていることがわかります。 型は常にnull以外であると想定していますが、JavaScriptで真にアサートすることはできません。 ユーザーは次のように定義できます。

function(myNonNull:!myClass):void {
  myNonNull.foo();
}

また、null以外として定義されているため、コンパイラーにとってはすべてが満足できるかもしれませんが、javascriptでそれを使用する他の誰かがnullとkaboomを渡します。

そうは言っても、おそらく解決策は、公開されているメソッドの場合、nullではなく自動的にアサートされる可能性があります。 しかし、コンパイラーは、強制できなかったために!nonnull宣言を持つことができるパブリックプロパティ(または実際にはプライベート)を持つことはできないと主張することもできます。

これは、これを適切に実施するためのコードコントラクトの議論に深く入り込む可能性があります。

私の批評家を許してください。代数的データ型がここにあるので、null許容型ではほとんど必要ないと思います。 人々が不足している値を表すためにnullを使用する理由は、JavaScriptとほとんどのOOP言語で同様にそれを行うためのより良い方法がないためです。 したがって、イメージングADTはすでにここにあります。 次に、null不可能なものの前に書かれた古いライブラリについては、それらを持っていても生活は良くなりません。 新しいライブラリに関しては、ADTを配置すると、nullをまったく使用せずに、ビジネスドメインの仕様に従って値が取ることができるものを非常に正確にモデル化できます。 つまり、私が言っているのは、ADTは同じ問題に対処するためのはるかに強力なツールだということだと思います。

個人的には、 Maybe<T>インターフェイスを少し作成し、そのタイプの変数がnullにならないように規律を使用しています。

文字列以外の型は本質的にnull許容であるため、例として使用することをお勧めします。 :P
「!」のユーザーとコンパイラーから、null許容型ではないことが問題になっていることがわかります。 型は常にnull以外であると想定していますが、JavaScriptで真にアサートすることはできません。 ユーザーは次のように定義できます。

function(myNonNull:!myClass):void {
myNonNull.foo();
}
また、null以外として定義されているため、コンパイラーにとってはすべてが満足できるかもしれませんが、javascriptでそれを使用する他の誰かがnullとkaboomを渡します。

私はあなたが定義することもできることを本当に理解していません:

function myFunc(str: string): int {
 return str && str.length;
}

誰かがintをその関数に渡すと、エラーも発生します。typescriptの利点は、javascriptで手動でチェックするものをコンパイラに委任し、null許容/非nullを再度チェックすることです。 -null許容型は私にとって合理的なようです。 ちなみに、SaferTypeScriptとClosureCompilerはすでにその種のチェックを行っています。

共用体型を使用すると、そのための非常に単純な仕様を作成できます。
基本的な型「null」があるとしましょう。「より厳密な」モードを使用できます。「null」と「undefined」はどの型とも互換性がないため、null許容値を表現する場合は次のようにします。

var myNullableString: (null | string);
var myString = "hello";
myNullableString = myString //valid
myString = myNullableString // error null is not assignable to string;

'strict mode'をアクティブにすると、typescriptは、null許容でないすべての変数が初期化されていることを確認する必要があります。また、デフォルトでは、オプションのパラメーターがnull可能です。

var myString: string; // error
var myNullableString: (null | string); // no error

function myString(param1: string, param2?: string) {
  // param1 is string
  // param2 is (null | string)
}

@fdecampredon +1

FacebookがTypeScript構文を使用しているがデフォルトでnull許容型ではないFlowについて示したIIRCは、元の投稿のように(null | T)省略形をサポートします- ?TまたはT?だったと思います

var myString: string; // error

変数を条件付きで初期化する場合、これは非常に煩わしい可能性があります。例:

var myString: string;
if (x) {
myString = a;
} else if (y) {
myString = b;
} else {
myString = c;
}

たとえば、Rustでは、myStringが使用される前に初期化されることをコンパイラが確認できる限り、これは問題ありませんが、TypeScriptの推論は現時点ではこれをサポートしていません。

正直なところのような何かやっvar myString = ''の代わりにvar myString: stringそんなに私を気にしないが、ルールのようなものが常に可能であることを確認してください。

@fdecampredon +1

省略形については(null | string)必ず?string結構です。
そして、 @ johnnyreillyはコンパイル時のチェックにすぎないことを確認してください

合計型は、デフォルトでnull許容型ではない可能性を非常に興味深いものにします。 デフォルトでnull許容でないの安全特性は誇張することはできません。 合計型に加えて、計画されている「if / typeof destructuring」(これを何と呼ぶべきかわからない)により、null許容APIと非null許容APIを統合することもタイプセーフになります。

ただし、型をデフォルトでnull不可にすることは、大きな重大な変更であり、既存のほとんどすべてのサードパーティ型定義ファイルを変更する必要があります。 私は100%重大な変更に取り組んでいますが、世の中に出回っている型定義を更新できる人は誰もいません。

これらの定義の大きなコンセンサスがDefinitelyTypedリポジトリに収集されているのは良いことですが、私はまだこの機能について実際的な懸念を持っています。

@samwgoldmanのアイデアは、 nonImplicitAnyような特別なコンパイラフラグの下でのみnull許容型を持たないことです。このフラグには、 strictまたはnonNullableTypeという名前を付けることができます。 したがって、重大な変更はありません。

@fdecampredon DefinitelyTypedのような非TypeScriptライブラリの型定義はどうですか? これらの定義はコンパイラによってチェックされないため、nullを返す可能性のあるサードパーティのコードは、正しく機能するために再注釈を付ける必要があります。

現在「文字列を返す」と注釈が付けられている関数の型定義を想像できますが、nullを返すこともあります。 nonNullableType 'edコードでその関数に依存している場合、コンパイラーは文句を言わず(どうしてですか?)、私のコードはnullセーフではなくなります。

何かが足りない場合を除いて、これはフラグでオンとオフを切り替えることができる機能ではないと思います。 これは、相互運用性を確保するためのオールオアナッシングのセマンティック変更であるように思われます。 ただし、フラグ切り替え機能が発生する可能性が高いと思うので、間違っていることが証明されれば幸いです。

余談ですが、FacebookのFlowコンパイラについてはまだ多くの情報がありませんが、プレゼンテーションのビデオ録画からは、デフォルトでnull不可であるように見えます。 もしそうなら、少なくともここにはいくつかの優先順位があります。

さて、 type | null | undefined省略形? typeがあるとしましょう。

@fdecampredon DefinitelyTypedのような非TypeScriptライブラリの型定義はどうですか? これらの定義はコンパイラによってチェックされないため、nullを返す可能性のあるサードパーティのコードは、正しく機能するために再注釈を付ける必要があります。

現在「文字列を返す」と注釈が付けられている関数の型定義を想像できますが、nullを返すこともあります。 NullableTypeを使用していないコードでその関数に依存している場合、コンパイラーは文句を言わず(どうすればよいでしょうか)、コードはnullセーフではなくなります。

問題はわかりません。一部の定義ファイルはnonNullableTypeモードでは有効ではありませんが、ほとんどの場合、優れたライブラリはnullまたはundefinedを返さないようにします。したがって、ほとんどの場合、定義は正しいままです。
とにかく、私が個人的にDefinitelyTyped定義を選択することはめったにありませんが、それをチェック/変更する必要はありません。いくつかの定義で?プレフィックスを追加するために少し余分な作業が必要になります。

何かが足りない場合を除いて、これはフラグでオンとオフを切り替えることができる機能ではないと思います。 これは、相互運用性を確保するためのオールオアナッシングのセマンティック変更であるように思われます。 ただし、フラグ切り替え機能が発生する可能性が高いと思うので、間違っていることが証明されれば幸いです。

フラグ切り替え機能を使用できなかった理由がわかりません。ルールは単純です。

  • 通常モードでは、 ? stringstringと同等であり、 nullまたはundefinedはすべてのタイプに割り当てることができます
  • NonNullableTypeモードでは、 ? stringstring | null | undefinedと同等であり、 nullまたはundefinednullまたはundefined以外のタイプに割り当てることはできません

フラグ切り替え機能との非互換性はどこにありますか?

言語のセマンティクスを変更するフラグは危険なものです。 1つの問題は、影響が潜在的に非常に非局所的であるということです。

function fn(x: string): number;
function fn(x: number|null): string;

function foo() {
    return fn(null);
}

var x = foo(); // x: number or x: string?

コードの一部を見ている人が型システムを「追跡」し、行われている推論を理解できることが重要です。 言語のルールを変更するフラグの束を持ち始めると、これは不可能になります。

唯一安全な方法は、割り当て可能性のセマンティクスを同じに保ち、現在のnoImplicitAnyように、エラーとフラグに依存しないものを変更することです。

レトロ互換性が損なわれることはわかっています。 flowtypeでそれを味わった後は、正直なところ非常に貴重な機能であり、typescriptの一部になることを願っています。

RyanCavanaughのコメントに加えて->私がどこかで読んだことから、ES7仕様/提案は関数のオーバーロードの使用に言及しています(同じ関数名ですが、入力パラメーターのデータ型が異なります)。 これは、Javascriptにとって非常に必要な機能です。

フロードキュメントから:

フローは、nullを他のタイプの一部ではない別個の値と見なします

var o = null;
print(o.x); // Error: Property cannot be accessed on possibly null value

?Tと書くことにより、任意のタイプTにnull(および未定義の関連値)を含めることができます。

var o: ?string = null;
print(o.length); // Error: Property cannot be accessed on possibly null or undefined value

[フロー]は、いくつかの動的タイプテストの効果を理解しています

(つまり、TS用語ではタイプガードを理解します)

var o: ?string = null;
if (o == null) {
  o = 'hello';
}
print(o.length); // Okay, because of the null check

制限事項

  • エイリアシングの可能性があるため、オブジェクトプロパティのチェックは制限されています。

Flowは、ローカル変数のタイプを調整できることに加えて、特にチェックと使用の間に中間操作がない場合に、オブジェクトプロパティのタイプを調整できることもあります。 ただし、一般に、オブジェクトのエイリアスは、この形式の推論の範囲を制限します。これは、オブジェクトプロパティのチェックが、エイリアスを介したそのプロパティへの書き込みによって無効になる可能性があり、静的分析でエイリアスを正確に追跡することが難しいためです。

  • タイプガードスタイルのチェックは、オブジェクトプロパティに対して冗長になる可能性があります。

[D] nullチェックが安全のために十分であることが明らかな場合でも、コード内の他のメソッドでnullチェックが実行されるため、null可能フィールドが一部のメソッドで非nu​​llとして認識されることを期待しないでください。実行時(たとえば、前者のメソッドの呼び出しは常に後者のメソッドの呼び出しに従うことがわかっているため)。

  • undefinedはチェックされません。

nullと同様に、未定義の値も問題を引き起こす可能性があります。 残念ながら、未定義の値はJavaScriptに遍在しており、言語のユーザビリティに深刻な影響を与えずにそれらを回避することは困難です。 たとえば、配列には要素用の穴を設けることができます。 オブジェクトのプロパティは動的に追加および削除できます。 この場合、フローはトレードオフになります。未定義のローカル変数と戻り値を検出しますが、オブジェクトプロパティと配列要素のアクセスに起因する未定義の可能性を無視します。

nullタイプ(および疑問符の省略形)を導入するときにオプションが同時に追加された場合はどうなりますか? ファイルにnull型が存在すると、コマンドラインにフラグが存在しない場合でも、コンパイラはそのファイルに対してnull許容モードになります。 それともそれは少し魔法すぎますか?

@jbondcは良さそうです。 ただし、それに関する問題は、どこでも!終わることです:p

It's tempting to want to change JavaScript but the reality is a 'string' is nullable or can be undefined.

これは何を意味するのでしょうか? jsには静的な型はありません。 したがって、はい、文字列は「null許容」ですが、文字列にも番号が付けられ、オブジェクト化可能で、だまされやすいことなどを忘れないでください。任意の値に任意のタイプを指定できます。

したがって、JavaScriptの上に静的型システムを階層化する場合、静的型をnull許容にするかどうかを選択することは、単なる設計上の決定です。 通常、指定された型に加えてnull値を受け入れるなど、関数のシグネチャが必要なのは特別な場合のみであるため、null許容型ではない方がデフォルトの方が適しているように思われます。

セマンティクスにスコープ変更を引き起こす「usestrict」のようなディレクティブは、すでに言語の一部です。 TypeScriptで「null不可の型を使用する」ディレクティブを使用するのが合理的だと思います。

@metaweta十分ではないと思います。たとえば、_non nullmodule_がnull許容モジュールを消費した場合はどうなりますか。

//module A
export function getData(): string[] {
  return null;
}
//module B
'use nonnull'
import A = require('./A');

var data: string[] = A.getData();

モジュールBのdataは実際にはnull許容ですが、モジュールAでは'use nonnull'が使用されていないため、エラーを報告する必要がありますか?
ディレクティブベースの機能でその問題を解決する方法がわかりません。

はい、

var data: string[] = A.getData();

エラーが発生します。 代わりに、 getData()null返す場合のデフォルト値を指定する必要があります。

var data: string[] = A.getData() || [];

@metawetaわかりましたが、それがエラーであることをどうやって知るのですか? :)
getDataのタイプはまだ '()=> string []'です。'nullableモジュール 'からのすべてを' nullable 'として自動的に処理しますか?

はい、その通りです(null許容モジュールの型が明示的にマークされていない限り)。

これは、そのファイルがデフォルトでnull可能かどうかを指示するファイルごとのフラグが必要なようです。

個人的には、この変更を導入するのは少し遅いと思います。 @ RyanCavanaughは正しいです。ファイルを見ただけでは何が起こっているのかを判断できないため、この変更により難しくなります。

プロジェクトは、デフォルトでこのコンパイラフラグをオンまたはオフにして開始しますか? 誰かがデフォルトのnull許容プロジェクトに取り組んでいて、デフォルトのnull許容プロジェクトを作成/切り替えると、混乱が生じますか?
私は現在、ほとんどのプロジェクトでNo Implicit Anyを使用していますが、そのオプションがオンになっていないプロジェクトに出くわすと、驚きます。

暗黙のないものは良いのですが、言語の振る舞いを変えるフラグに関しては、それが線であるべきだと思います。 それ以上に、異なるルールを持つ異なる人々によって開始された複数のプロジェクトに取り組んでいる人々は、誤った仮定とスリップアップのために多くの生産性を失うことになります。

@RyanCavanaughは非局所性を懸念しており、ディレクティブは字句スコープになっています。 各サイトに注釈を付けない限り、これ以上ローカルを取得することはできません。 私はこの指令に特に賛成ではありません。 私は、このオプションが存在し、少なくともES5の「厳密に使用する」と同じくらい合理的であることを指摘していました。 私は個人的にデフォルトでnull許容型ではないことを支持していますが、実際にはそれには遅すぎます。 これらの制約を考えると、私は!を使用することに賛成です。 何とかして。 @jbondcの提案により、nullとundefinedを区別できます。 Javaバックエンドが人々に両方の値を使用させ続けることを考えると、それは私にとって最も有用であるように思われます。

はっきりしなかった場合は申し訳ありませんが、私はライアンに同意し、自分自身の懸念を追加していました。

正直なところ、 use not-null追加することが、すべてのnullポインター例外を回避するための代償である場合、 nullまたはundefinedを任意の型に割り当て可能であると考えると、問題なく支払うことになります。私の意見では、そのタイプスクリプトが作成されました。

@jbondc

nullでない場合、プログラマーが作成する構文には影響しませんが、そのコードを使用しようとする次のプログラマーの機能に影響します(作成者とユーザーが別々の人であると想定)。

したがって、コード:

function myfoo (mynumber: number) {
    return !!mynumber;
} 

(電話での入力は間違っている可能性があります)
通常のプロジェクトとnotnullプロジェクトの両方で有効なコードです。 コーダーがコードが機能しているかどうかを知る唯一の方法は、コマンドライン引数を調べることです。

作業中は、テストプロジェクト(新機能のプロトタイピングを含む)とメインプロジェクト(実際のコードを使用)があります。 プロトタイプをあるプロジェクトから別のプロジェクトに移動する準備ができたら(通常は大きな屈折望遠鏡を使用)、コードにエラーはありませんが、コードの使用にエラーがあります。 この動作は、暗黙的なものがないこととは異なり、strictを使用すると、両方ともすぐにエラーになります。

今、私はこれらのプロジェクトにかなりの影響力を持っているので、時間を節約できないので、この新しい「機能」を使用しないように担当者に警告することができますが、仕事。
1つのプロジェクトでもこの機能を有効にする場合は、プロジェクト間で非常に多くのコード共有とコード移行が行われるため、他のすべてのプロジェクトで有効にする必要があります。この「機能」により、多くのことが発生します。戻って、すでに終了した機能を「修正」する時間の。

右@jbondc。 @Griffork :申し訳ありませんが、その誤解は

"use not-null";
// All types in this program production (essentially a single file) are not null

function f(n: number) {
  "use not-null";
  // n is not null and local variables are not null
  function g(s: string) {
    // s is not null because g is defined in the scope of f
    return s.length;
  }
  return n.toFixed(2);
}

function h(n: number) {
  // n may be null
  if (n) { return n.toFixed(3); }
  else { return null; }
}

null許容型ではありません。 null許容型はuselesです。 それらは役に立たない。 使い物にならない! あなたはそれを理解していませんが、あなたは本当にそれらを必要としません。 これからはNULLを使用しないと宣言して、自分自身を制限する意味はほとんどありません。 たとえば、存在しない部分文字列を見つけようとしている状況で、欠落している値をどのように表現しますか? 欠落している値(今のところNULLが行うこと)を表現できないことは、問題を解決することにはなりません。 どこでもNULLのある過酷な世界を、欠測値がまったくない同じように過酷な世界と交換します。 本当に必要なのは代数的データ型と呼ばれ、(他の多くのクールなものの中でも)欠落している値(最初に探しているものと命令型の世界ではNULLで表されるもの)を表す機能を備えています。 よく知られた問題に対する素朴で厄介な解決策である役に立たない構文/意味論的ゴミのように見えるので、私は言語にnull不可能なものを追加することに強く反対しています。 F#のオプションとHaskellの多分、バリアント(タグ付き共用体、識別された共用体)とパターンマッチングについて読んでください。

@ aleksey-bykovJavaScriptにundefinednull 2つのnullの値があることに気付いていないようです。 JavaScriptのnull値は、一致しない正規表現で、JSONで日付をシリアル化するときにのみ返されます。 それがその言語である唯一の理由は、Javaアプレットとの相互作用のためでした。 宣言されているが初期化されていない変数はundefinedであり、 nullではありません。 オブジェクトにプロパティがない場合は、 nullではなくundefined返されます。 undefined有効な値にしたい場合は、 propName in objテストできます。 プロパティが継承されているのではなく、オブジェクト自体に存在するかどうかを確認する場合は、 obj.hasOwnProperty(propName)ます。 欠落している部分文字列は-1を返します: 'abc'.indexOf('d') === -1

Haskellでは、普遍的なサブタイプがないからこそ、たぶん便利です。 Haskellのボトム型は、ユニバーサルサブタイプではなく、非終了を表します。 代数的データ型が必要であることに同意しますが、整数でラベル付けされたツリーが必要な場合は、すべてのノードにnullundefinedではなく整数を持たせたいと思います。 それらが必要な場合は、Maybeintまたはzipperというラベルの付いたツリーを使用します。

「usenot-null」ディレクティブを採用する場合は、「use not-void」(nullでもundefinedでもない)も必要です。

nullから独自のコードを保証したい場合は、nullを禁止するだけです
リテラル。 null許容型を開発するよりもはるかに簡単です。未定義は
もう少し複雑ですが、それらが何から来ているのかを知っているなら
あなたはそれらを避ける方法を知っています。 Haskellのボトムはかけがえのないものです!私は望む
JavaScript(TypeScript)には、値のないグローバルスーパータイプがありました。恋しい
式を投げる必要があるときはひどい。 TypeScriptを使用しています
v 0.8以降、nullを使用したことはなく、nullを必要としていました。無視してください
withような他の役に立たない言語機能と同じように
声明。

@ aleksey-bykovライブラリを作成していて、入力がnullでないことを保証したい場合は、どこでもそのライブラリのランタイムテストを実行する必要があります。 コンパイル時のテストが必要です。提供するのは難しくありません。ClosureとFlowの両方で、null以外/未定義の型がサポートされます。

@ metawetapleaseNonNullablesNumbersOnly(<any> null) 。 jsにコンパイルした後は、ルールはまったくありません。 第二に、なぜあなたは気にしますか? 大声でそれを言うと先行クリアnulls are not supported, you put a null you will get a crash免責条項のように、あなたはそこに人々のすべての並べ替えから自分を保証することはできませんが、責任のあなたのスコープの概要をすることができます。 第三に、ユーザーが入力として入力する可能性のあるものすべてに対して防弾である主要な主流のlibを考えることはほとんどできませんが、それでも非常に人気があります。 それで、あなたの努力はトラブルの価値がありますか?

@ aleksey-bykovライブラリのクライアントもタイプチェックされている場合は、nullが発生しないことを保証できます。 それがTypeScriptの要点です。 あなたの推論によれば、タイプはまったく必要ありません。ドキュメントで予想されるタイプが何であるかを「大声ではっきりと言ってください」。

トピックから外れて、nullは、未定義に対してチェックするよりも高速であるため、私たちにとって非常に価値があります。
どこでも使用するわけではありませんが、初期化されていない値や欠落している数値を表すために、可能な限り使用するようにしています。

トピックについて:
nullが他のコードに「エスケープ」する問題は発生していませんが、ランダムな未定義またはNaNが表示される問題が発生しています。 このシナリオでは、慎重なコード管理の方がフラグよりも優れていると思います。

ただし、ライブラリ型付けの場合は、nullを返す可能性のある関数に注釈を付けることを選択できるように、冗長型nullを使用すると便利です(これはコンパイラーではなく、コーディング手法によって強制されます)。

@metaweta 、私の考えでは、クライアントはコードベースでnullを使用しないでください。。null (大文字と小文字を区別し、単語全体)を完全に検索して、すべて削除してください。 不格好すぎる? 空想のためにコンパイラスイッチ--noNullLiteralを追加します。 他のすべてはそのままで、同じコードで、心配する必要はなく、最小限のフットプリントではるかに軽量なソリューションです。 私のポイントに戻ると、null許容型ではないタイプがTSに到達し、2つの異なるフレーバーで利用可能であると仮定します。

  • !構文を使用して、nullを取得できない型を示すことができます。たとえば、 string!はnullを取得できません。
  • noNullsAllowedスイッチがオンになっている

次に、サーバーからajaxを介してjsonの一部を取得し、どこにでもnullがあります。道徳的です。javascriptの動的な性質は、その上にある型アノテーションでは修正できません。

@ aleksey-bykov同様に、数値プロパティxを持つオブジェクトを期待していて、サーバーから{"x":"foo"}を取得した場合、型システムはそれを防ぐことができません。 。 サーバーでTypeScript以外のものを使用する場合、これは必然的に実行時エラーであり、避けられない問題です。

ただし、サーバーがTypeScriptで記述され、ノードで実行されている場合は、フロントエンドコード用の.d.tsファイルが存在する状態でトランスパイルできます。タイプチェックにより、サーバーがnullを含むJSONを送信しないことが保証されます。その中またはxプロパティが数値ではないオブジェクト。

@ metaweta 、null許容でない型は確かに別の型安全性対策になるでしょう、私はそれを

はい、これはひどく必要です。 Hoareによる10億ドルの間違いを参照してください。 nullポインタ例外(NPE)は、null許容型と非null許容型を区別しない型付きプログラミング言語で発生する最も一般的なエラーです。 それは非常に一般的であるため、Java 8はそれと戦うための必死の試みでOptionalを追加しました。

型システムでnullableをモデル化することは、単なる理論上の懸念ではなく、大幅な改善です。 コードでnullを回避するように細心の注意を払っていても、使用するライブラリはそうではない可能性があるため、型システムを使用してデータを適切にモデル化できると便利です。

TSにユニオンとタイプガードが存在するようになったので、タイプシステムはこれを行うのに十分強力です。 問題は、下位互換性のある方法で実行できるかどうかです。 個人的には、この機能はTypeScript2.0がこの点で下位互換性を持たないようにするために十分に重要であると感じています。

この機能を適切に実装すると、既存のコードを壊すのではなく、すでに壊れているコードを指す可能性があります。単に、それらの外部にnullをリークする関数(ほとんどの場合、意図せずに)またはメンバーを適切に初期化しないクラス(この部分)を指します。型システムは、コンストラクターで初期化されるメンバー値を考慮に入れる必要がある場合があるため、より困難です)。

これは、nullを_使用しない_ことではありません。 関係するすべてのタイプを適切にモデル化することについてです。 実際、この機能により、安全な方法でnullを使用できるようになります。nullを回避する理由はもうありません。 最終結果は、代数のMaybe型でのパターンマッチングと非常によく似ています(ただし、case式ではなくifチェックを使用して実行されます)。

そして、これはnullリテラルだけではありません。 nullundefinedは構造的に同じであるため(一方には機能するが他方には機能しない関数/演算子はありません)、TSの単一のnull型で十分にモデル化できます。

@metaweta

JavaScriptのnull値は、一致しない正規表現で、JSONで日付をシリアル化するときにのみ返されます。

まったく真実ではありません。

  • DOMとの相互作用により、nullが生成されます。
  console.log(window.document.getElementById('nonExistentElement')); // null
  • @ aleksey-bykovが上で指摘したように、ajax操作はnullを返す可能性があります。実際、 undefinedは有効なJSON値ではありません。
 JSON.parse(undefined); // error
 JSON.parse(null); // okay
 JSON.stringify({ "foo" : undefined}); // "{}"
 JSON.stringify({ "foo" : null}); // '{"foo":null}'

注意:存在しないプロパティにアクセスするとundefinedなるため、 undefinedがajax経由で返されるように見せかけることができます。これがundefinedがシリアル化されない理由です。

ただし、サーバーがTypeScriptで記述され、ノードで実行されている場合は、フロントエンドコード用の.d.tsファイルが存在する状態でトランスパイルできます。タイプチェックにより、サーバーがnullを含むJSONを送信しないことが保証されます。その中またはxプロパティが数値ではないオブジェクト。

これは完全に正しいわけではありません。サーバーがTypeSciptで記述されている場合でも、永続ストレージから取得したすべてのオブジェクトのすべてのプロパティをチェックしない限り、nullが導入されることを保証することはできません。

これについては@ aleksey-bykovに同意します。コンパイル時にnullやundefinedによって発生したエラーについてTypeScriptに警告してもらうことができれば絶対に素晴らしいですが、nullの実際のソースが検出されないまま、誤った自信を引き起こし、雑学クイズになってしまうのではないかと心配しています。

サーバーがTypeSciptで記述されている場合でも、永続ストレージから取得したすべてのオブジェクトのすべてのプロパティをチェックしない限り、nullが導入されることを保証することはできません。

これは実際には、null許容型ではない引数です。 ストレージがnullFooを返すことができる場合、そのストレージから取得されるオブジェクトのタイプは、FooではなくNullable <Foo>です。 次に、Fooを返すことを意図した関数を返す場合は、nullを処理することによって責任を負う必要があります(よく知っているためにキャストするか、nullをチェックします)。

null許容型がない場合は、格納されたオブジェクトを返すときにnullをチェックする必要はありません。

私はそれが誤った自信を誘発し、ヌルの本当の原因が検出されないままトリビアを捕まえることになるのではないかと心配しています。

null許容型ではないタイプは、どのような種類のトリビアではないと思いますか?

これは完全に正しいわけではありません。 サーバーがTypeSciptで記述されている場合でも、永続ストレージから取得したすべてのオブジェクトのすべてのプロパティをチェックしない限り、nullが導入されることを保証することはできません。

永続ストレージが型付きデータをサポートしている場合は、必要ありません。 ただし、そうでない場合でも、データフェッチポイントでのみチェックを行い、他のコードのすべてのコードで保証を行うことができます。

これについては@ aleksey-bykovに同意します。 コンパイル時にnullやundefinedによって発生したエラーについてTypeScriptに警告してもらうことができれば絶対に素晴らしいですが、nullの実際のソースが検出されないまま、誤った自信を引き起こし、雑学クイズになってしまうのではないかと心配しています。

null許容型を使用することは絶対的な要件ではありません。 メソッドが「重要ではない」ためにnullを返す場合をモデル化する必要がないと思われる場合は、その型定義でnull許容型を使用することはできません(そしていつもと同じ安全性が得られません)。 しかし、このアプローチが失敗すると考える理由はありません-すでにそれJetBrainsによるKotlin

@ aleksey-bykov正直なところ、完全に間違っています。null許容型ではないことの最も良い点の1つは、_型をnull許容型として表現できることです_。
nullポインタエラーを防ぐためにnullを決して使用しないという戦略では、エラーが発生することを恐れてnullを使用する可能性を完全に失います。これは完全にばかげています。

言語機能についての議論でもう一つお願いします:のような愚かなコメントと一緒に行かないでください:

null許容型ではありません。 null許容型はuselesです。 それらは役に立たない。 使い物にならない! あなたはそれを理解していませんが、あなたは本当にそれらを必要としません。

それはあなたがウェブ上のどこにでも投稿するものを無視するべきだと私に感じさせます、私たちは機能について議論するためにここにいます、私はあなたの視点が私のものではないことを理解し、喜んで受け入れますが、子供。

null以外の型の注釈を導入することにまったく反対していません。 C#やその他の言語で役立つことが示されています。

OPは、次のように議論のコースを変更しました。

正直なところ、use not-nullを追加することが、すべてのnullポインター例外を回避するための代償である場合、nullまたはundefinedを任意の型に割り当て可能と見なすと、問題なく支払うことができます。

私は単に、野生でのnullundefinedの蔓延を指摘していました。

また、TypeScriptについて私が本当に感謝していることの1つは、言語の自由放任主義の態度です。 それは新鮮な空気の息吹でした。

型がデフォルトでnull許容ではないと主張することは、その精神の本質に反します。

私は、なぜこれを行う/必要としないのかについて多くの議論が浮かんでいるのを見てきました。これまでに議論されてきた根本的なケースをすべて理解しているかどうかを確認したいと思います。

1)関数がnullを返すことができるかどうかを知りたい(入力ではなく実行パターンが原因)。
2)値がnullになる可能性があるかどうかを知りたい。
3)データオブジェクトにnull値を含めることができるかどうかを知りたい。

現在、状況2が発生する可能性があるのは2つのケースのみです。nullを使用している場合と、関数がnullを返す場合です。 コードからすべてのnullを削除すると(それらが不要であると想定して)、実際には状況2は状況1の結果としてのみ発生する可能性があります。

状況1null値の存在を示すために、関数の戻り型に注釈を付けることによって最もよく解決されると思います。 _これは、null以外の型が必要であることを意味するものではありません_。 関数に注釈を付けることができ(たとえば、共用体型を使用して)、null以外の型を持たないようにすることができます。これは、ドキュメントと同じですが、この場合はおそらくより明確です。

解決策2もこれによって解決されます。

これにより、会社で働くプログラマーは、プロセスと標準を使用して、Typescriptチームではなく、null型がマークアップされるように強制できます(タイピングシステム全体がオプトインであるのとまったく同じ方法で、明示的なnull許容型がオプトインになります)。の)。

シナリオ3に関しては、サーバーとクライアント間の契約はTypescriptが強制するものではなく、影響を受ける値をnullの可能性があるものとしてマークアップできるようにすることで改善される可能性がありますが、最終的にはTypescriptと同じgarenteeを取得します。ツールは、他のすべての価値を提供します(つまり、優れたコーディング標準またはプラクティスがない限り、価値はありません。

(電話からの投稿、エラーでごめんなさい)

@fdecampredon 、そもそも恐れではなく、nullを使用する必要がないということです。 私はそれらを必要としません。 素晴らしいボーナスとして、null参照例外の問題が解消されました。 どうすればそれはすべて可能ですか? 大文字と小文字が空の合計タイプを使用する。 合計型は、F#、Scala、HaskellなどのすべてのFP言語のネイティブ機能であり、代数的データ型と呼ばれる製品型と一緒になっています。 大文字と小文字が空の合計タイプの標準的な例は、F#のオプションであり、Haskellの場合もあります。 TypeScriptにはADTがありませんが、代わりに、ADTがカバーするであろう1つの特別なケースをモデル化する非null可能オブジェクトの追加について進行中の議論があります。 だから私のメッセージは、ADTのnull不可能なものを捨てることです。

@spion 、悪いニュース:F#はnullを取得しました(.NETのレガシーとして)。 良いニュース:誰もそれらを使用しません。 nullが存在する場合、どうしてnullを使用できないのでしょうか。 それらにはオプションがあります(あなたが言ったように最新のJavaのように)。 したがって、自由に使えるより良い選択肢があれば、nullは必要ありません。 これが私が最終的に提案していることです。null(null以外)をそのままにして、存在することを忘れて、言語機能としてADTを実装します。

nullを使用しますが、ユーザーが使用する方法と同じではありません。 私の会社のソースコードは2つの部分に分かれています。

1)データ(データベースデータなど)に関しては、変数宣言時にnullを空白データに置き換えます。

2)プログラム可能なオブジェクトのように、他のすべてのオブジェクトはnullを使用するため、必要なjavascriptオブジェクトではないオブジェクトが作成または割り当てられないソースコードにバグや抜け穴がある場合がわかります。 未定義は、ソースコードにバグまたは抜け穴があるjavascriptオブジェクトの問題です。

null許容にしたくないデータは、顧客データであり、nullの文言が表示されます。

@ aleksey-bykov typescriptには共用体型のadtがあり、欠落しているのはパターンマッチングだけですが、これは元のソースに近いjavascriptを生成するというtypescript哲学では実装できない機能です。

一方、共用体型でnull値の除外を定義することは不可能です。そのため、null以外の型が必要です。

@ fdecampredon 、TSにはADTがなく、合計型ではない共用体があります。これは、1。ユニット型がないため、空のケースを正しくモデル化できない、2。

ADTのパターンマッチングは、生成されたJavaScriptと緊密に連携する方法で実装できますが、とにかく、この議論がターニングポイントではないことを願っています。

@ aleksey-bykovこれはF#ではありません。 JavaScriptのタイプをモデル化することを目的とした言語です。 JavaScriptライブラリはnull値と未定義値を使用します。 したがって、これらの値は適切なタイプでモデル化する必要があります。 ES6でさえ代数的データ型をサポートしていないため、 TypeScriptの設計目標を考えると、そのソリューションを使用することは意味がありません。

さらに、JavaScriptプログラマーは通常、パターンマッチングの代わりに、ifチェック(typeofおよびequalityテストと組み合わせて)を使用します。 これらはすでにTypeScript共用体タイプを絞り込むことができます。 この時点から、代数的な多分などに匹敵する利点を持つnull許容型をサポートするためのほんの小さなステップです。

lib.d.tsが導入する必要のある大きな変更や、コンストラクターの呼び出し中のクラスフィールドの一時的なnull状態の潜在的な問題について、実際に誰も言及していないことに驚いています。 これらは、null許容型ではない型を実装するための実際の潜在的な問題です...

@Grifforkのアイデアは、コード内のいたるところにnullチェックがないようにすることです。 次の機能があるとしましょう

declare function getName(personId:number):string|null;

名前がnullかどうかを一度だけチェックし、nullチェックを追加する必要があるという心配なしに、残りのすべてのコードを実行するという考え方です。

function doSomethingWithPersonsName(personId:number) {
  var name = getName(personId);
  if (name != null) return doThingsWith(name); // type guard narrows string|null to just string
  else { return handleNullCase(); }
}

そして今、あなたは素晴らしいです! 型システムは、 doThingsWithがnull以外の名前で呼び出されることを保証します

function doThingsWith(name:string) {
  // Lets create some funny versions of the name
  return [uppercasedName(name), fullyLowercased(name), funnyCased(name)]
}

これらの関数はいずれもnullをチェックする必要はなく、コードはスローせずに機能します。 そして、これらの関数の1つにnull許容文字列を渡そうとするとすぐに、型システムはエラーが発生したことをすぐに通知します。

function justUppercased(personId:number) {
  var name = getName(personId);
  return uppercasedName(name); // error, passing nullable to a function that doesn't check for nulls.
}

これは大きな利点です。型システムは、関数がnull許容値を処理できるかどうかを追跡し、さらに、実際に処理する必要があるかどうかを追跡します。 よりクリーンなコード、より少ないチェック、より多くの安全性。 そして、これは文字列だけではありません-ランタイムタイプチェックのようなライブラリを使用すると、はるかに複雑なデータのタイプガードを構築することもできます

また、null値の可能性をモデル化する価値がないと感じたために追跡が気に入らない場合は、古き良き危険な動作に戻すことができます。

declare function getName(personId:number):string;

そのような場合、typescriptは、明らかに間違っていることをした場合にのみ警告を表示します

uppercasedName(null);

率直に言って、下位互換性を除いて、マイナス面は見られません。

@jbondchttps://github.com/Microsoft/TypeScript/issues/186が1つあります

@fdecampredon共用体型はまさにそれです、共用体。 それらは_disjoint_unions akasumsではありません。 #186を参照してください。

@ aleksey-bykov

オプションタイプの追加は、依然として重大な変更になることに注意してください。

// lib.d.ts
interface Document {
    getElementById(id: string): Maybe<Element>;
}

...

// Code that worked with 1.3
var myCanvas = <HTMLCanvasElement>document.getElementById("myCanvas");
// ... now throws the error that Maybe<Element> can't be cast to an <HTMLCanvasElement>

結局のところ、あなたは今、破壊することで貧乏人のオプションタイプを手に入れることができます

class Option<T> {
    hasValue: boolean;
    value: T;
}

var { hasValue, myCanvas: value } = <Option<HTMLCanvasElement>> $("myCanvas");
if (!hasValue) {
    throw new Error("Canvas not found");
}
// Use myCanvas here

しかし、これからの唯一の値は、lib.d.ts(および他の.d.ts、およびコードベース全体ですが、それを修正できると想定します)もそれを使用するかどうかです。そうでない場合は、 Optionを使用しない関数は、コードを確認しない限り、nullを返すかどうかを返すことができます。

また、デフォルトでnull以外の型を使用することには賛成しないことに注意してください(とにかくTS 1.xでは使用できません)。 大きな変化です。

しかし、2.0について話しているとしましょう。 とにかく重大な変更(オプション型の追加)を行う場合は、型をデフォルトでnull不可にしないのはなぜですか? タイプをデフォルトでnull不可にし、オプションタイプを追加することは排他的ではありません。 後者はスタンドアロンにすることができますが(たとえば、ご指摘のとおりF#で)、前者には後者が必要です。

@Arnavion 、いくつかの誤解があります。署名を置き換える必要があるとは言いませんでした。既存のすべての署名はそのまま残ります。新しい開発のために、ADTまたはその他の必要なものを自由に使用できます。 したがって、重大な変更はありません。 デフォルトでは、null以外になるものはありません。

ATDがここにある場合、nullがアプリケーションにリークする可能性のあるすべての場所を、オプションに変換してラップするのは開発者の責任です。 これは、スタンドアロンプ​​ロジェクトのアイデアになる可能性があります。

@ aleksey-bykov

署名を置き換える必要があるとは言いませんでした。既存の署名はすべてそのまま残ります

_I_は署名を置き換える必要があると言ったので、その理由をすでに説明しました。

しかし、これからの唯一の値は、lib.d.ts(および他の.d.ts、およびコードベース全体ですが、それを修正できると想定します)もそれを使用するかどうかです。そうでない場合は、 Optionを使用しない関数は、コードを確認しない限り、nullを返すかどうかを返すことができます。


デフォルトでは、null以外になるものはありません。 これまで。

TS 1.xの場合、重大な変更が大きすぎるという理由だけで同意します。 2.0の場合、デフォルトの署名(lib.d.tsなど)でオプションタイプを使用することは、すでに重大な変更になります。 型をデフォルトでnull不可にすることは、それに加えて価値があり、欠点はありません。

私は同意しません。オプションを導入しても何も壊れてはなりません。オプション、null可能、またはnull不可能を使用するわけではありません。 誰もが好きなものを使います。 物事を行う古い方法は、新しい機能に依存するべきではありません。 差し迫ったニーズに適したツールを使用するかどうかは、開発者次第です。

つまり、Option <number>を返す関数fooとnumberを返す関数barがある場合、barの実装を確認するか、ドキュメントを維持しない限り、barがnullを返すことができないと確信することはできません。 「この関数がnullを返すことはありません。」? これはnullを返さない関数を罰すると思いませんか?

あなたの例の関数barは、時間の始まりからnull許容として知られていました。これは、世界中の約100500のアプリケーションで使用され、誰もが結果をnull許容として扱いました。今、あなたはその中を見て、nullを発見しました。不可能ですが、先に進んで署名をnull許容から非null可能に変更する必要があるということですか? 私はあなたがすべきではないと思います。 この知識は価値がありますが、100500のアプリケーションにブレーキをかける価値がないためです。 あなたがすべきことは、次のように署名が改訂された新しいライブラリを考え出すことです。

old_lib.d.ts

...
declare function bar(): number; // looks like can return a potentially nullable number
...

Revision_lib.d.ts

declare function bar(): !number; // now thank to the knowledge we are 100% certain it cannot return null

現在、レガシーアプリはold_lib.d.tsを使用し続け、新しいアプリの場合、開発者はrevised_libs.d.ts自由に選択できます

残念ながら、revized_libs.d.tsにはまだ調べていない50の関数があり、それらはすべて数値を返します(ただし、それが実際に数値なのかnull許容型な数値なのかはわかりません)。 今何?

さて、時間をかけて、助けを求め、バージョン管理を使用してください(これまでに得た知識のレベルに応じて、バージョン番号を増やしながら段階的にリリースすることをお勧めします: revised_lib.v-0.1.12.d.ts

実際には必要ありません。 シグニチャでnull許容型を返すが、実装でnull許容型を返さない関数は、呼び出し元による冗長なエラーチェックのみをもたらします。 安全性を損なうことはありません。 徐々に多くの関数に!で注釈を付けます。 あなたが言ったように、あなたがそれらを発見すると、それらはうまく機能します。

私はファンではありません! 入力するのが手荷物が多いという理由だけで(キーストロークとそれを使用することを覚えておく必要があるという点で)。 1.xで非ヌル可能性が必要な場合は、! はすでに上で説明したオプションの1つですが、2.0で最終的に重大な変更を加え、非null可能性をデフォルトにすることは価値があります。

一方、Python 2/3風の状況につながる可能性があります。この状況では、すべての変数宣言とクラスを確認するために数百万行のコードベースを実行する余裕がないため、何年もの間TS2.0にアップグレードする人は誰もいません。メンバーと関数のパラメーターと...には? nullになる可能性がある場合。 2to3(Python 2から3への移行ツール)でさえ、そのような広範囲の変更に対処する必要はありません。

2.0が重大な変更を行う余裕があるかどうかは、TSチームによって異なります。 はいに投票しますが、修正が必要な100万行のコードベースがないため、投票はカウントされない可能性があります。

おそらく、Funscriptの人々に、ヌルを返すDOM APIをF#とどのように調整するかを尋ねる必要があります(FunscriptはF#コードから使用するためにTypeScriptのlib.d.tsと他の.d.tsを使用します)。 私はこれを使用したことはありませんが、たとえばhttp://funscript.info/samples/canvas/index.htmlを見ると、タイププロバイダーはdocument.getElementsByTagName( "canvas")[0]が未定義。

編集:ここでは、document.getElementById()がnullを返すことは期待されていないようです。 少なくとも、結果で.onlickにアクセスしていることを確認すると、Option <Element>を返しているようには見えません。

@spion
おかげで、私はそれについて考えていませんでした。

仕事で私たちのコードベースは小さくありません、そして人々が望んでいるこの壊滅的な変化は私たちをほとんど利益なしで多くの時間を後退させるでしょう。 優れた標準と開発者間の明確なコミュニケーションにより、nullが本来あるべき場所に表示されるという問題は発生していません。
一部の人々がそれをひどく推し進めていることに正直驚いています。
言うまでもなく、これは私たちにほとんど利益をもたらさず、私たちに多くの時間を費やすことになります。

@Griffork、typescriptコンパイラでこの

破損に関しては、既存の型定義を使い続けると、コンパイラが初期化されていない可能性のある変数やフィールドのリークを指摘する場合を除いて、エラーがまったく発生しない可能性があると思います。 一方、特にクラスフィールドがコードのコンストラクターで初期化されないままになっている場合(後で初期化される場合など)、多くのエラーが発生する可能性があります。 したがって、私はあなたの懸念を理解しており、TS1.xの下位互換性のない変更を要求していません。 言語への変更が下位互換性を壊す価値があるなら、それはこれであると私がなんとかあなたを説得できたことを私はまだ望んでいます。

いずれにせよ、 Facebookのフローにはnull許容型ではありません。 成熟したら、この問題を気にする私たちのTSの代わりとして調査する価値があるかもしれません。

@spion 、あなたのリストが私たちに与える唯一の数字は、何らかの理由でnullまたはundefinedが言及された回数です、基本的にはnullとundefinedが話されたとだけ言っています、あなたがそれを引数として使用できないことは明らかだと思います

@ aleksey-bykov私はそうではありません-そのフィルタリングされたリストのすべての問題を調べました。「crash」という単語が含まれているすべての問題は、関数がプロパティまたはメソッドにアクセスしようとしたことを示すスタックトレースに関連していました。未定義またはnull値の。

私はさまざまなキーワードでフィルターを絞り込もうとしましたが、私は(私が思うに)それらすべてを取得することができました

@spion
質問:これらのエラーのうち、変数をnull可能または定義不可能としてマークする必要がある場所で発生するエラーはいくつありますか?

EGオブジェクトが親を持つことができ、親をnullに初期化し、常にnullの親を持つオブジェクトを1つ持つ場合でも、親をnullの可能性があると宣言する必要があります。
ここでの問題は、ループがnullの親に到達する前に常にループが中断することを想定して、プログラマーがコードを記述した場合です。 これは、nullが言語に含まれている場合の問題ではなく、 undefinedでもまったく同じことが起こります。

nullを現在と同じように使いやすくする理由:
•未定義よりも優れたデフォルト値です。
。 1)場合によってはチェックが高速です(コードは非常にパフォーマンスが高い必要があります)
。 2)inループがオブジェクトに対してより予測可能に機能するようにします。
。 3)(欠落値ではなく)空白値にnullを使用する場合、配列の使用がより理にかなっています。 delete array[i]array[i] = undefinedは、indexOf(およびおそらく他の一般的な配列メソッド)を使用する場合の動作が異なることに注意してください。

nullを作成した結果、言語で使用するには追加のマークアップが必要だと私は感じています。
•nullエラーではなく未定義のエラーが発生しました(これはほとんどのtypescriptシナリオで発生します)。

nullのエスケープに問題がないと言ったとき、nullに初期化されていない変数がnullになることはなく、未定義のエラーが発生するのとまったく同じ場所でnull例外エラーが発生することを意味しました(Typescriptチームと同様) )。 nullを使いにくくし(追加の構文を必要とすることにより)、未定義のままにしておくと、実際には一部の開発者(たとえば、私たち)にとってより多くの問題が発生します。

nullを使用するために構文を追加すると、数週間/数か月間、多くのnullを使用する開発者は、新しい構文を覚えようとしているときに、左、右、中央にエラーが発生します。 そして、それは将来(何かを少し間違って注釈することによって)スリップする別の方法になるでしょう。 [タイプを表すために記号を使用するという考えが嫌いであると指摘するとき、それは言語をより明確にしません]

nullが原因で、undefinedでは発生しないエラーや問題が発生する状況を説明できるまで、nullをundefinedよりも使用しにくくすることに同意しません。 それにはユースケースがあり、それがあなたを助け

結論:
未定義でない型を定義できなくても、null許容型ではない型を宣言できることには意味がありません。 また、JavaScriptの動作方法により、未定義の型は使用できません。

@Grifforkは、nullFacebookFlowの実装を、それが非常に

使用が少し難しくなる_only_ケースはこれです:この変数に一時的にnullを割り当ててから、nullでないかのようにこの他のメソッドで使用しますが、これまでこの他のメソッドを呼び出すことは決してないことを知っています最初に変数を初期化するため、nullをチェックする必要はありません。 これは非常に脆弱なコードであり、コンパイラがそれについて警告してくれることを歓迎します。とにかくバグになることから離れたリファクタリングです。

@spion
私はあなたがどこから来ているのかをようやく理解したと信じています。

値がnullにならない場合を判断し、内部でnullをチェックしない関数を呼び出せるようにするために、型ガードが必要だと思います。 そして、保護するifステートメントが削除されると、それはエラーになります。

それが役に立つことがわかります。

また、これはあなたが望んでいる特効薬にはならないだろうとも信じています。

コードを実行せず、コードのすべての側面をテストしないコンパイラーは、コードを作成したプログラマーよりも、未定義/ヌルがどこにあるかを判断するのに優れているわけではありません。 この変更により、人々が誤った安心感に陥り、実際にnull /未定義のエラーが発生したときに追跡が困難になるのではないかと心配しています。
本当に、あなたが必要とする解決策は、Typescriptをサポートする優れたテストツールのセットであり、その約束を果たせないコンパイラに型を実装するのではなく、Javascriptでこの種のバグを再現できると思います。

あなたはフローがこの問題の解決策を持っていると言いますが、あなたのリンクを読んだとき、私は物事に関するいくつかを見ました:

「フローは、特にチェックと使用の間に中間操作がない場合に、オブジェクトプロパティのタイプを調整することもあります。ただし、オブジェクトプロパティのチェックでは、オブジェクトのエイリアスによってこの形式の推論の範囲が制限される場合があります。エイリアスを介したそのプロパティへの書き込みによって無効になり、静的分析でエイリアスを正確に追跡することは

「nullと同様に、未定義の値も問題を引き起こす可能性があります。残念ながら、未定義の値はJavaScriptに遍在しており、言語の使いやすさに深刻な影響を与えずに回避することは困難です[...]この場合、フローはトレードオフになります。未定義のローカル変数と戻り値を検出しますが、オブジェクトプロパティと配列要素のアクセスに起因する未定義の可能性を無視します。」

未定義とnullの動作が異なり、未定義のエラーがどこにでも表示される可能性があり、疑問符は値がnullでないことを保証できず、言語はJavascript(TSが私が見たものから避けようとしているもの)とはより異なる動作をします)。

ps

foo(thing: whatiknowaboutmyobject) {
    if (thing.hidden) {
        delete thing.description;
    }
}

if (typeof thing.description === "string") {
    //thing.description is non-nullable now, right?
    foo(thing);
    //What is thing.description?
    console.log(thing.description.length);
}

TSはすでにエイリアシング効果に対して脆弱です(可変値を許可する他の言語も同様です)。 これはエラーなしでコンパイルされます:

function foo(obj: { bar: string|number }) {
    obj.bar = 5;
}

var baz: { bar: string } = { bar: "5" };

foo(baz);

console.log(baz.bar.charAt(0)); // Runtime error - Number doesn't have a charAt method

フロードキュメントは、完全を期すためにこれを述べています。

編集:より良い例。

年:

はい、しかし私は、何かを未定義に設定する手段は、何かを別の値に設定する手段よりもはるかに優れていると主張します。

つまり、

mything.mystring = 5; // is clearly wrong.
delete mything.mystring; //is not clearly wrong - this is not quite the equivalent of setting mystring to >undefined.

編集:
まあ、この時点でそれはかなり個人的な好みです。 何年もの間javascriptを使用した後、私はこの提案が言語に役立つとは思わない。 それは人々を誤った安心感に陥らせるだろうと思います、そしてそれはTypescript(言語として)をJavascriptから遠ざけるだろうと思います。

@Griffork現在のタイプスクリプトが誤った安心感に陥る例については、遊び場で提示した例を試してください。

var mything = {mystring: "5"}; 
delete mything.mystring;
console.log(mything.mystring.charAt(1));

ちなみに、削除演算子はnull型の値を割り当てるのと同じように扱うことができ、それでケースもカバーできます。

言語がJavaScriptとは異なる動作をするという主張は真実ですが、意味がありません。 TypeScriptはすでにJavaScriptとは異なる動作をしています。 型システムのポイントは、常に意味のないプログラムを禁止することでした。 null許容型ではない型をモデル化すると、いくつかの追加の制限が追加されます。 null許容型の変数へのnullまたは未定義の値の割り当てを禁止することは、文字列型の変数への数値の割り当てを禁止することとまったく同じです。 JSは両方を許可しますが、TSはどちらも許可しません

@spion

アイデアは、コードのいたるところにnullチェックがないようにすることです

あなたが提唱していることを私が理解している場合:

A.デフォルトですべてのタイプをnull以外にします。
B.null許容のフィールドと変数をマークします。
C.アプリケーション/ライブラリ開発者がアプリケーションへのすべてのエントリポイントをチェックしていることを確認します。

しかし、それは、コードにnullがないことを保証する責任は、コンパイラーではなく、コードを作成する人にあるという意味ではありませんか? コンパイラに「dw、システムにnullを入れない」と効果的に伝えています。

別の方法としては、nullはどこにでもあるので、気にしないでください。ただし、nullにできないものがある場合は、お知らせします。

事実、後者のアプローチでは参照例外がnullになる傾向がありますが、より真実です。 ワイヤー(つまりajax)を介して取得されたオブジェクト上のフィールドがnullでないふりをすることは、神への信仰を持っていることを意味します:smiley:。

上記の項目Cは、作業内容によっては些細なことでも実行不可能なこともあるため、この問題については強い意見の相違があると思います。

@jbondcあなたがそれを聞いてくれてうれしいです。 実際、CallExpressionは未定義またはnull許容としてマークされています。 ただし、型システムは現在、それを利用していません。それでも、nullまたは未定義ではないかのように、 typeArgumentsに対するすべての操作を許可します。

ただし、null許容型ではない型と組み合わせて新しい共用体型を使用する場合、型はNodeArray<TypeNode>|nullとして表すことができます。 次に、型システムは、nullチェックが適用されない限り、そのフィールドに対する操作を許可しません。

if (ce.typeArguments != null) {
  callSomethingOn(ce.typeArguments)
}

// callSomethingOn doesn't need to perform any checks

function callSomethingOn(na:NodeArray<TypeNode>) {
...
}

TS 1.4タイプガードの助けを借りて、ifブロック内で、式のタイプがNodeArray<TypeNode>絞り込まれ、そのタイプに対するすべてのNodeArray操作が可能になります。 さらに、そのチェック内で呼び出されるすべての関数は、これ以上チェックを実行しなくても、引数タイプをNodeArray<TypeNode>に指定できます。

しかし、あなたが書き込もうとすると

function someOtherFunction(ce: CallExpression) {
  callSomethingOn(ce.typeArguments)
}

コンパイラはコンパイル時にそれについて警告します、そしてバグは単に起こらなかったでしょう。

いいえ、 @ NoelAbrahams 、これは確実にすべてを知ることではありません。 他のすべてのタイプと同様に、変数またはフィールドに含めることができる値を判別するのに役立つコンパイラーについてです。

もちろん、外部値では、いつものように、それらのタイプが何であるかを指定するのはあなた次第です。 外部データには数値ではなく文字列が含まれているといつでも言うことができます。コンパイラは文句を言いませんが、数値に対して文字列操作を実行しようとするとプログラムがクラッシュします。

しかし、null許容型がなければ、値をnullにすることはできないと言うことさえできません。 null値はすべてのタイプに割り当てることができ、制限を加えることはできません。 undefinedはどのタイプでも有効な値であるため、変数は初期化されないままにしておくことができ、警告は表示されません。 したがって、コンパイラは、コンパイル時にnullおよび未定義関連のエラーをキャッチするのを支援できません。

null許容型ではないタイプについて多くの誤解があるのは驚くべきことです。 これらは、初期化されないままにすることも、値nullまたはundefined割り当てることもできないタイプにすぎません。 これは、文字列を数値に割り当てることができないこととそれほど違いはありません。 私は実際にはどこにも到達していないと感じているので、これはこの問題に関する私の最後の投稿です。 誰かがもっと知りたいと思っているなら、私は前述の「10億ドルの間違い」のビデオから始めることをお勧めします。 この問題はよく知られており、多くの現代言語やコンパイラーによってうまく対処されています。

@spion 、型がnullになるかどうかを述べることができることのすべての利点について、私は完全に同意します。 しかし、問題は、型をnull以外にするかどうかです_

ワイヤーを介して取得されたオブジェクト(つまりajax)のフィールドがヌルではないふりをすることは、神を信じることを意味します

だからふりをしないでください。 null許容としてマークすると、null不可として使用する前にテストする必要があります。

もちろん。 つまり、フィールドをnull可能としてマークするか(フィールドがデフォルトでnull以外であるシステムの場合)、またはフィールドをnull不可としてマークするか(フィールドがデフォルトでnull可能であるシステムの場合)になります。

前者は重大な変更であり(重要である場合とそうでない場合があります)、アプリケーション開発者がすべてのエントリポイントをチェックして保証する必要があるため、受け入れられないという議論があります。

@NoelAbrahamsなぜそれが「無効」であるのか、基本的にほとんどの場合nullを望まないのかわかりません。また、エントリポイントがnull返す可能性がある場合は、最終的には、null以外の型の型システムです。デフォルトでは、アプリケーション内のいくつかのapi / library / entryポイントを信頼できるため、nullチェックを少なくすることができます。

null許容型システムで型を非nullとしてマークすることについて少し考えると、値が制限されますが、テストを強制されることなく、null許容型の変数/戻り型を使用できます。
また、ほとんどの場合、適切に設計されたライブラリがnullや未定義の値を返すことはないため、定義の作成者はより多くのコードを記述する必要があります。
最後に、概念さえ奇妙です。null許容型ではない型システムでは、null許容型は共用体型として完全に表現できます。 ?stringstring | null | undefinedと同等です。 null許容型をデフォルトとしてnull許容型としてマークできる型システムでは、 !stringどのように表現しますか? string - null - undefined

結局、私はここの人々の懸念を本当に理解していません。 nullは文字列ではありません。同じように、 5は文字列ではなく、両方の値は文字列ではありません。文字列が期待されている場合に使用、およびせるスリップするvar myString: string = nullようがちなエラーとして次のとおりです。 var myString: string = 5
nullまたは未定義を任意のタイプに割り当てることができるのは、おそらく開発者が精通している概念ですが、それでも悪い概念です。

私は以前の投稿で完全に正しかったとは思いません。私はその時間にそれを非難します。

コードの一部を調べて、どのように機能するかを確認しました。たとえば、次のように、特定のフィールドをnull許容としてマークすることは確かに役立ちます。

interface Foo {
        name: string;
        address: string|null; /* Nullable */
}

var foo:Foo = new FooClass();
foo.name.toString(); // Okay
foo.address.toString(); // Error: do not use without null check

しかし、私が反対するのは次のとおりです。

foo.name = undefined; // Error: non-nullable

これはJavaScriptの自然な操作方法を妨げると思います。

まったく同じことが番号にも当てはまります:

interface Foo {
        name: string;
        address: string|number; 
}
var foo:Foo = new FooClass();
foo.name.toString(); // Okay
foo.address.slice() // error

foo.name  = 5 // error

そしてそれはJavaScriptでもまだ有効です

合理的に、オブジェクトのプロパティにnullを喜んで割り当てる回数はどれくらいですか?

ほとんどのものはnullとしてマークされると思いますが、フィールドがnull不可であると宣言するために型ガードに依存することになります。

@fdecampredon
実際にはかなりたくさん。

@Griffork

ほとんどのものはnullとしてマークされると思います

それが私の最初の考えでした。 しかし、コードのいくつかのセクションを調べた後、次のようなコメントを見つけました。

interface MyType {

     name: string;

     /** The date the entry was updated from Wikipedia or undefined for user-submitted content. */
     wikiDate: Date; /* Nullable */
}

フィールドがnull許容であるという考えは、情報を提供するためによく使用されます。 また、TypeScriptは、 wikiDateにアクセスするときに型ガードが必要な場合、エラーをキャッチします。

@fdecampredon

foo.name = 5 // error
そしてそれはJavaScriptでもまだ有効です

本当ですが、TypeScriptは意図的ではないことを100%確実に認識しているため、これはエラーです。

一方

foo.name = undefined; //サーバーに名前を送信しない

完全に意図的です。

私たちの要件に最も近い実装は、共用体型を使用しないことだと思いますが、元の提案に従ってください。

 wikiDate: ?Date;

@NoelAbrahamsに同意します

foo.name = 5 //エラー
そしてそれはJavaScriptでもまだ有効です
本当ですが、TypeScriptは意図的ではないことを100%確実に認識しているため、これはエラーです。

コンパイラは、ちょうどあなたのように名前をマークしていることを知っているstringなくstring | numberあなたがNULL可能値をしたい場合は、ちょうどとしてそれをマークします?stringまたはstring | nullいます(ほぼ同等です)

私たちの要件に最も近い実装は、共用体型を使用しないことだと思いますが、元の提案に従ってください。

wikiDate:?Date;

したがって、タイプはデフォルトでnull以外であり、 ? :)でnull許容とマークすることに同意します。
?DateDate | null | undefinedと同等であるため、共用体型になることに注意してください:)

申し訳ありませんが、私はデフォルトでnull可能であり、特別な入力ではnullではないことに同意しようとしていました(記号は混乱しています)。

@fdecampredon 、実際には、フィールドまたは変数がnull可能としてマークされている場合、アクセスには型ガードが必要です。

var wikiDate: ?Date;

wikiDate.toString(); // error
wikiDate && wikiDate.toString(); // okay

これは重大な変更ではありません。これは、まだ実行できるはずだからです。

 var name: string;   // okay
 name.toString();  // if you think that's fine then by all means

おそらく、 nullを共用体型に導入せずにこれを実現することはできないと思いますか?

あなたがするときあなたの最初の例は絶対に正しいです:

wikiDate && wikiDate.toString(); // okay

タイプガードを使用すると、コンパイラは何も警告しません。

ただし、2番目の例は良くありません

var name: string;   // okay
name.toString();  // if you think that's fine then by all means

コンパイラにはここでエラーが発生するはずです。単純なアルゴリズムでは最初の行でエラーが発生する可能性があり(ユニタライズされた変数はnull許容としてマークされていません)、より複雑なアルゴリズムでは最初の使用前に割り当てを検出しようとする可能性があります。

var name: string;   // okay
name.toString();  // error because not initialized
var name: string;
if (something) {
  name = "Hello World";
} else {
  name = "Foo bar";
}
name.toString();  // no error since name will always be initialized.

バリアをどこに置くかは正確にはわかりませんが、開発者の邪魔にならないように、何らかの微妙な調整が必要になるでしょう。

それは破壊の変化だと2.0の前に導入することができない、@metawetaが提案した「使用NULL以外の」ディレクティブで、おそらく除く外

持っていない理由:

var string1: string; //this works like typescript does currently, doesn't need type-guarding before use, null and undefined can be assigned to it.
string1.length; //works
var string2: !string; //this doesn't work because the string must be assigned to a non-null and non-undefined value, doesn't need type-guarding before use.
var string3: ?string; //this must be type guarded to non-null, non-undefined before use.
var string4: !string|null = null; //This may be null, but should never be undefined (and must be type-guarded before use).
var string5: !string|undefined; //this may never be null, but can be undefined (and must be type-guarded before use).

また、-noinferrednullsを示すコンパイラフラグ(-noimplicitanyがオンの場合にのみ機能します)を用意します。これにより、型(stringやintなど)の通常の構文が無効になり、?を指定する必要があります。 また ! それらを使用します(null、未定義、および例外であるすべてのタイプ)。

このように、null不可はオプトインであり、コンパイラフラグを使用して、プロジェクトを明示的にnullにすることができます。
コンパイラは、(前の提案のように)後ではなく、型

考え?

編集:これを書いたのは、null以外を使用するという考えをすべてのアクションで明示的にする必要があるためです。 他のTSプロジェクトからのコードを読む人なら誰でも、何が起こっているのかを正確に知ることができます。 また、コンパイラフラグがオンになっていると非常に明白になります( blah: stringはエラーですが、 blah:!stringはエラーではないため、-noimplicitanyが機能するのと同じです)。

Edit2:
DefinatelyTypedは、非推論ヌルをサポートするようにアップグレードできます。ユーザーがオプトインしないことを選択した場合、ライブラリの使用は変更されません。 と ! 特徴。

null以外とundefined以外がオプトイン、オプトアウト、
型修飾子(!?)、ディレクティブ、またはコンパイラーフラグ。 何でもします
_表現できる限り_それらを取得するのにかかりますが、そうではありません
現在の場合。

2時35分PMに月、2014年12月22日には、Griffork [email protected]書きました:

持っていない理由:

var string1:文字列; //これはtypescriptが現在行っているように機能します。使用前に型保護を必要とせず、nullとundefinedを割り当てることができます。
string1.length; // worksvar string2:!string; //文字列はnullでも未定義でもない値に割り当てる必要があるため、これは機能しません。use.varstring3:?string;の前に型保護は必要ありません。 //これは、使用する前にnull以外、未定義でないように型ガードする必要があります。varstring4:!string | null = null; //これはnullの可能性がありますが、未定義にすることはできません(使用する前に型保護する必要があります)。varstring5:!string | undefined; //これはnullになることはありませんが、未定義にすることができます(使用する前に型保護する必要があります)。

そして、(-noimplicitanyがオンになっている場合にのみ機能する)コンパイラフラグを設定します。
-noinferrednullsと言います。これは、型の通常の構文を無効にします(
string、およびint)であり、?を指定する必要があります。 また ! それらと一緒に(null、未定義
および例外)。

このように、null不可能なものはオプトインであり、コンパイラを使用できます
プロジェクトを明示的にnullにするフラグ。
コンパイラは、後ではなく、_タイプの割り当て_でエラーにフラグを立てます(
前の提案)。

考え?

このメールに直接返信するか、GitHubで表示してください
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -67899445

マイクステイ[email protected]
http://www.cs.auckland.ac.nz/~mike
http://reperiendi.wordpress.com

@Grifforkはオプションですが、私の意見では悪いものです。理由を説明します。

  • 正しい定義を持つためにすべてのタイプをチェックして注釈を付ける必要があるため、定義ファイルでより多くの作業が発生します。
  • 最終的には、コードのいたるところに!string (場合によっては?string )を書き込むことになり、コードが読みにくくなります。
  • 型がnull許容であるシステムの!stringは奇妙な概念であり、実際に記述できない唯一の方法はstring minus null minus undefined 。それどころか、 ?stringは非常に簡単に記述できます。型がデフォルトでnullである型システムstring | null | undefined
  • string | nullはタイプガードが必要であるが、 stringは必要ないことをコンパイラが理解するタイプチェックアルゴリズムを見つけるために、多くの頭痛(およびパフォーマンスの低下)が予想されます。基本的に、次のような概念を導入します。一部の共用体タイプは、他の共用体タイプとは異なる方法で処理する必要があります。
  • そして最後に、型推論を完全に失う最悪の部分var myString = "hello" myStringはstring?stringまたは!stringますか? 正直なところ、ここでの視点では大きな頭痛の種です。

デフォルトとしてnull以外の型がない場合、ここで見た中で最も良い提案は、@ metawetaによって提案された'use non-null'ディレクティブです。
確かに適切に指定する必要がありますが、少なくともすべてのファイルにuse non-null文字列を含めるだけで、単純で予測可能な動作を得ることができます。

@fdecampredon

  1. 定義ファイルではもっと多くの作業が必要になるかもしれませんが、_とにかくその作業を行う必要があります_(型が正しいことを確認するため)そして今回はコンパイラーがまだ編集していないものを通知します(-noimplicitnullを使用している場合) )。
  2. 注釈については、他の提案も受け付けています。 私は正直に言って、現在の型システムにはその場所があり、_置き換え_すべきではないと信じています。 大きな変化はそれだけの価値はないと思います。 代わりに、私たちはあなたが何を求めているのかを説明するためのより良い方法を見つけるべきだと思います。 (私は、これを記号で表すというアイデアは本当に嫌いです。直感的ではありません。)
  3. それについて説明するのは難しいですか? このリクエスト(特定のタイプ)が(マーカーなしで)提案されているタイプスクリプトの他の場所での議論を見てきました。 クイック検索を行いましたが、問題が見つかりませんでした。後で詳しく調べます。

  4. 私が!string|nullと書いたものを参照している場合、nullが_like_ {}として扱われた場合(ただし、それに割り当てられなかった場合)、現在のシステムで機能します。
    私のリストになかったstring|nullについて話しているのなら、この場合はnullを無視する必要があると思います。 nullとundefinedは、すべてのnon-nullとunundefinedの前に!が付いている共用体でのみ意味があります。 そして、anyはanyではありません(これはコンパイラの警告/エラーである可能性があります)。
  5. 良い質問です。-noimplicitnullオプションを使用している場合にのみ発生する質問です。最も安全なオプションは、初期エラー(おそらくnull可能)を引き起こす可能性が最も高いオプションに割り当てることだと思いますが、私が考えていないより良いアイデアがあると感じています。 他の誰かがこれにどのようにアプローチすべきかについての提案があるのだろうか?

編集:ポイント2に追加されました。
編集:ポイント4のタイプミスを修正しました。

定義ファイルではもっと多くの作業が必要になるかもしれませんが、とにかくその作業を行う必要があり(型が正しいことを確認するため)、今回はコンパイラーがまだ編集していないものを通知します(-noimplicitnullを使用している場合) )。

はい、いいえ。使用しているライブラリをチェックして、実際にnullまたは未定義を返す量を確認してください。
これは非常にまれなケースです。この問題では、標準ライブラリの発生はごくわずかであり、たとえばPromiseライブラリでは発生しません。
私のポイントは、型がデフォルトでnull許容ではない型システムでは、既存の定義ファイルのほとんどがすでに有効であるということです

注釈については、他の提案も受け付けています。 私は正直に言って、現在の型システムにはその場所があり、置き換えるべきではないと信じています。 大きな変化はそれだけの価値はないと思います。 代わりに、私たちはあなたが何を求めているのかを説明するためのより良い方法を見つけるべきだと思います。 (私は、これを記号で表すというアイデアは本当に嫌いです。直感的ではありません。)

そのような方法はないと思いますが、私の意見では計り知れない価値があるので、私が間違っていることを願っています。
重大な変更の部分については、なぜ'use non-null'ディレクティブに反対するのですか?
null許容型システムを希望する開発者はまったく影響を受けません(ファイルの先頭にすでに奇妙なことに'use non-null'を追加している場合を除きますが、正直言ってそれは少し...奇妙です)
また、null以外の型システムを希望する開発者は、新しいディレクティブを使用できます。

それについて説明するのは難しいですか? このリクエスト(特定のタイプ)が(マーカーなしで)提案されているタイプスクリプトの他の場所での議論を見てきました。 クイック検索を行いましたが、問題が見つかりませんでした。後で詳しく調べます。
私は概念が少し「奇妙」であまり明確ではないと感じています。私は通常、プログラミングのメインツールとして合成を使用しますが、_分解_ではありませんが、そうではありません。

私が書いたものを!string | nullとして参照している場合、nullが{}のように扱われると(ただし、それに割り当てることはできませんでした)、現在のシステムで機能します。 私のリストになかったstring | nullについて話しているのなら、この場合はnullを無視すべきだと思います。 nullとundefinedは、すべてのnon-nullとunundefinedの前に!が付いている共用体でのみ意味があります。 そして、anyはanyではありません(これはコンパイラの警告/エラーである可能性があります)。
私はあなたが3つのタイプを分解するときという事実に言及しています:

  • !stingstring - null - undefined
  • stringstring | null | undefined
  • ?stringstring | null | undefined

最新の2つには基本的に違いはありませんが、コンパイラは、 string場合、タイプガードチェックを強制してはならず、 ?string場合、その情報をどこにでも伝播する必要があることを知っている必要があります。アルゴリズムは実際よりもはるかに複雑になり、型推論で奇妙なケースを見つけることができると確信しています。

良い質問です。-noimplicitnullオプションを使用している場合にのみ発生する質問です。最も安全なオプションは、初期エラー(おそらくnull可能)を引き起こす可能性が最も高いオプションに割り当てることだと思いますが、私が考えていないより良いアイデアがあると感じています。 他の誰かがこれにどのようにアプローチすべきかについての提案があるのだろうか?

それは基本的に、 @ RyanCavanaughがデフォルトとしてnullからデフォルトとしてnullに切り替えることを可能にするフラグを導入することを考えたときにコメントしたのと同じ問題を
この場合、前者の提案ははるかに単純でした。

繰り返しになりますが、なぜ'use non-null'ディレクティブに反対しているのですか。考えれば考えるほど、理想的な解決策のように思えます。

@fdecampredon
現在提案されている「null以外の使用」は、言語の使用方法を変更するため、言語の記述方法を変更するのではありません。 つまり、ある場所から別の場所に移動したときの関数は、_使用済み_の場合とは異なる動作をする可能性があります。 何かが間違って入力されたために、1〜3ファイル離れている可能性のあるコンパイル時エラーが発生します。

の違い:
string

?string
それですか? と ! symbolは、この変数の厳密な型チェックを要求しています( "use nonnull"ディレクティブのように、変数ごとに)。 そのように説明すれば、人々がそれを理解するのにそれほど問題はないと思います。

違いはもちろんです:
ファイル1:

//...187 lines of code down...
string myfoo(checker: boolean) {
    if(checker){
        return null;
    }
    else {
        return "hello";
    }
}

ファイル2:

"use nonnull"
//...2,748 lines of code down...
string myfoo(checker: boolean) {
    if(checker){
        return null; //Error!
    }
    else {
        return "hello";
    }
}

開発者は、どのファイルがnullでないか、どのファイルがnullでないかについてのメンタルマップを保持する必要があります。 私はこれは悪い考えだと正直に信じています(たとえほとんどの_your_コードが 'use nonnull'であっても)。

編集:また、関数の入力を開始して、関数定義が何であるかを示す小さなウィンドウが表示された場合、その定義のstringがnull許容か非null可能かをどのように知ることができますか?

@Griffork

  • 繰り返しますが、これはすでに'use strict'です。
  • 少なくともアイデアは単純です。 型システムに簡単な概念を導入します。
  • 開発者が他のファイルの関数を移動する場合、私にとってはかなりエッジケースのように思われます。エラーが表示されるので、nullチェックについてのプロンプトが表示されるので、何が起こっているのかをすぐに理解できます...

繰り返しますが、それは完璧ではありませんが、より良い代替案は見当たりません。

明確にするために、私が見ることができる(反論の後の)私が提案したものに関するあなたの唯一の問題は次のとおりです:

  • var mystring = "string"の動作がどうなるかわかりません
  • どこにでも記号を入力する必要はありません。

そして、ディレクティブに関する私の懸念は次のとおりです。

  • null以外は明示的ではありません(ただし、null可能と同じプロジェクトで発生する可能性があります)。 編集:より意味をなすように文言を修正しました。
  • 開発者は、自分が書いているものがnull許容であるか、null許容ではないかを追跡するのが難しくなります。
  • 関数を呼び出したときに表示される関数定義(編集:関数の入力を開始したときにVisual Studioによって提供されるポップアップ)は、null許容である場合とそうでない場合があり、わかりません。
  • 関数が2つまたは3つのレイヤーにラップされると、その定義がまだ正しいかどうかはわかりません(「null以外の使用」がないファイルでは型推論を使用できないため)。

正直なところ、「厳密な使用」の実装は、目指すべきものではあり
そして、なぜディレクティブが開発者に彼らの意図について明示的にさせるよりも優れているのか分かりません(結局のところ、それがTypescriptが作成された理由ですよね?)。

編集:ポイントを明確にしました。
編集:文字列を引用符で囲みます

正直なところ、私の懸念の要約は少し小さく、私が書いたものを反映していません。あなたの提案は型システムに複雑さを追加し、型チェックアルゴリズムを頭痛の種にし、すべてのエッジケースで指定するのはかなり難しいです型推論のために特別に作成し、コードを超冗長にします。 基本的に、その仕事に適したツールではありません。

null以外のすべてを明示的にする必要があります(null可能と同じプロジェクトで発生する可能性があるため)。

stringが実際にはstring | null | undefinedあるという事実を、明示的にチェックするように強制することなく望んでいます。

開発者は、自分が書いているものがnull許容であるか、null許容ではないかを追跡するのが難しくなります。

プロジェクトが非ヌル性を使用している場合、「非ヌルを使用」せずにプロジェクトに単一のファイルがあるとは思えません。ファイルの上部をスクロールするのはそれほど難しくありません(少なくとも500行未満のコードでファイルを作成する場合)これはケースの大部分です...)

関数を呼び出したときに表示される関数定義は、null許容である場合とそうでない場合があり、わかりません。

はい、それがディレクティブ 'use non-null'を持つモジュールからのものである場合は、それに応じて入力されます。すべてをnull可能と見なさない場合は...

関数が2つまたは3つのレイヤーにラップされると、その定義がまだ正しいかどうかはわかりません(「null以外の使用」がないファイルでは型推論を使用できないため)。

ここであなたの主張がわかりません。

正直なところ、「厳密な使用」の実装は、目指すべきものではありません。 これは(Typescriptではなく)JavaScript用に設計されており、JavaScriptにはいくつかの貴重な選択肢があります。 私たちはTypescriptを使用しているので、物事をより良くするオプションがあります。
そして、なぜディレクティブが開発者に彼らの意図について明示的にさせるよりも優れているのか分かりません(結局のところ、それがTypescriptが作成された理由ですよね?)。

TypeScriptはjavascriptのスーパーセットであり、JavaScriptにルーツがあり、より安全な方法でjavascriptを記述できるようにすることを目的としているため、JavaScriptの概念を再利用することは不合理ではないようです。

そして、なぜこのディレクティブが開発者に彼らの意図について明示的にさせるよりも優れているのか分かりません

同じ結果を達成することはできないので、null許容型システムにnull許容型を持たないことを引用したすべての理由から、これは悪い考えです。

私の観点から、ここに3つの実行可能な解決策があります:

  • タイプがnull不可になる2.0の重大な変更
  • 私が数十のコメントを提案したように、デフォルトでnull許容型に切り替わるコンパイラフラグですが、 @ RyanCavanaughはそれに
  • 'null以外の'ディレクティブを使用

ちなみに、旗は私にとってそれだけの価値があります。

function fn(x: string): number;
function fn(x: number|null): string;

function foo() {
    return fn(null);
}

var x = foo(); // x: number or x: string?

関数が唯一の懸念事項である場合、オーバーロードに明示的なnull引数が含まれている場合は例外が発生する可能性があります。これは、暗黙のnullよりも常に優先されます( --noImplicitNullと調和するため)。 。 これは、ユーザーにとって意味のある解釈でもあります(つまり、「明示的に言うことは、暗黙的に言われることを上書きする必要があります」)。 この方法では解決できないフラグに関する他の同様の問題があるのではないかと思いますが。 そしてもちろん、これは仕様と実装の両方にいくらかのハック的な複雑さを追加します:|

  1. string|null|undefinedは、フラグを使用した私の提案で明示的だったので、それを省略しました。
  2. では、なぜファイルごとにそれプロジェクト全体に強制される
  3. 作成していないファイルをたくさん使用しています。 その特定のファイルに「null以外の使用」があることをどうやって知ることができますか? 他の人が作成した100個のファイルがある場合、それらの100個のファイルのうちどれがnullでないかを記憶する必要がありますか? (または、_毎回_別のファイルの変数/関数を使用しているので、そのファイルを開いて確認する必要がありますか?)
  4. これをより明確にできるかどうかを見てみましょう。投稿の最後にある例です。
  5. どこで止まりますか? どこが悪いと思いますか? ファイルの先頭にある1つの文字列またはキーワードによって、ファイルの動作が変わることはありません。 「usestrict」がjavascriptに追加されました。

    1. 当時、必要なことを実行できるJavascriptの大規模なスーパーセット(Typescriptなど)は存在しませんでした。

    2. これはJavaScriptの処理

      「usestrict」が採用されたのは、それが正しいことだったからではなく、ブラウザが開発者の要求に応えることができる

  6. Javascriptは「null許容型システム」ではなく、Typescriptは「null許容型システム」ではありません。 null許容型ではない型を宣言する機能を導入しても、システム全体が「null許容型ではない」という意味ではありません。 それはTypescriptのポイントではありません。
File 1:
"use notnull"
export string foo() {
    return "mygeneratedstring";
}
File 2:
export string foo() {
    return file1.foo()
}
File 3:
"use notnull"
file2.foo(); //???

同じ構文を使用しながら、実際にはコンテキスト情報


私たちは輪になって議論しているようです。 意味がわからない場合は申し訳ありませんが、同じ問題を繰り返し提起しているようで、繰り返し返信しています。

@spion
その例では、文字列は暗黙のnullではありません(--noImplicitNullフラグがオンになっていることを前提としています)

string | null | undefinedは、フラグを使用した私の提案で明示的でした。そのため、私はそれをouのままにしました。

つまり、実際のシステムでは、 var myString: stringは暗黙的にvar myString: (string | null | undefined);あり、その上、他のすべての共用体タイプでない限り、コンパイラーはタイプガードの使用を強制しません。

では、なぜファイルごとにそれを持っているのですか? 私にとって、そしておそらく他の多くの開発者にとって重要な下位互換性を壊さないので、私は私の提案を提案しています。 そして、それはプロジェクト全体に強制される可能性があります。
ディレクティブは下位互換性を壊しません(文字列リテラル 'use not-null'をディレクティブの位置で使用した場合を除きますが、これは非常に奇妙な理由で、誰もしません)。また、ある時点でTypeScriptは下位互換性を破る必要があります。そのため、semverはメジャーバージョンを定義しましたが、それは別の話です。

作成していないファイルをたくさん使用しています。 その特定のファイルに「null以外の使用」があることをどうやって知ることができますか? 他の人が作成した100個のファイルがある場合、それらの100個のファイルのうちどれがnullでないかを記憶する必要がありますか? (または、別のファイルの変数/関数を使用するたびに、そのファイルを開いて確認する必要がありますか?)

すべてのプロジェクトが正常に編成されているようなガイドラインなどのプロジェクトに参加している場合は、ご存知のとおりです...最悪の場合、少しスクロールする必要があります...それほど難しくはないようです...

どこで止まりますか? どこが悪いと思いますか? ファイルの先頭にある1つの文字列またはキーワードによって、ファイルの動作が変わることはありません。 「usestrict」がjavascriptに追加されました。
当時、必要なことを実行できるJavascriptの大規模なスーパーセット(Typescriptなど)は存在しませんでした。
これはJavaScriptの処理を高速化する試みでした(私の目には、JavaScriptが許される唯一の理由です)。 「厳密に使用する」が採用されたのは、それが正しいことではなく、ブラウザが開発者の要求に応えることができる唯一の方法だったからです。 typescriptが、任意のスコープで宣言された文字列として言語が機能する方法を根本的に変更し、他の任意のスコープに影響を与える他のディレクティブを1つ(次に2、次に3、次に4)追加するのを見たくありません。 それは本当に悪い言語デザインです。 「usestrict」がTypescriptに存在せず、代わりにコンパイラフラグであった場合(そしてTypescriptがそれを必要とするすべてのファイル/スコープにそれを発行した場合)、私は幸せです。

「usestrict」は、言語の動作を変更し、レトロな互換性を維持するために導入されました。これは、現在直面している問題とまったく同じであり、es6モジュールで多かれ少なかれ消えます(したがって、null以外の使用はtypescriptの次のメジャーバージョンで消える可能性があります) 。

Javascriptは「null許容型システム」ではなく、Typescriptは「null許容型システム」ではありません。 null許容型ではない型を宣言する機能を導入しても、システム全体が「null許容型ではない」という意味ではありません。 それはTypescriptのポイントではありません。

その文は私には意味がありません。JavaScriptには静的型システムがないので、 stringより前の変数にnullを割り当てることができるという事実は簡単に比較できます。以前は文字列だった変数に5を割り当てることができるという事実に。
唯一の違いは、JavaScript用に作成された型チェッカーであるTypeScriptが、 null5ではなく、すべてに割り当て可能であると考えていることです。

あなたの例では、はい、ファイル間の情報が失われています。これは私にとって許容できるトレードオフですが、あなたの懸念は理解できます。

私たちは輪になって議論しているようです。 意味がわからない場合は申し訳ありませんが、同じ問題を繰り返し提起しているようで、繰り返し返信しています。

はい、同意します。コンセンサスが得られるとは思えません。正直なところ、コンパイラをフォークして、null以外の型を自分で追加して、いつかメインコンパイラまたはそのフローになることを期待しています。使用できるほど成熟している。

@Grifforkちなみに@ spionのアイデアは機能すると思いますが、フラグstringが暗黙のnull許容型でない場合、 number | nullは常に最初のオーバーロードで優先されるため、両方でケースfoo(null)stringます。

@RyanCavanaughあなたはそれがうまくいくと思いますか?

@fdecampredon

  1. javascriptを最適化するために「usestrict」が導入されました。 それはブラウザによって消費されなければならなかったので、それはインコードでなければなりませんでした。 また、下位互換性が必要だったため、機能しないステートメントである必要がありました。
    null許容型ではないタイプは、ブラウザーで使用する必要がないため、コード内ディレクティブを使用する必要はありません。
  2. 申し訳ありませんが、投稿するのを忘れていたものについて言及していました。

Facebookによると、「JavaScriptでは、nullは暗黙的にすべてのプリミティブ型に変換されます。また、任意のオブジェクト型の有効な住民でもあります。」
null許容型を明示的にしたい理由は(開発者が行うすべてのアクションで)、null許容型を使用することにより、開発者はjavascriptの動作方法から逸脱していること、および型がnull不可であることを認識しているためです。保証されていません(これがうまくいかない原因となる可能性のあるものがたくさんあるため)。
null許容でないプロパティは、削除でき

私はかなり長い間Typescriptを使用しています。 最初に上司にJavascriptからTypescriptに変更するよう説得するのは困難でした。また、重大な変更があった場合はいつでもアップグレードできるように上司を説得するのは非常に困難でした(簡単ではない場合でも)。 ES:Harmonyがブラウザでサポートされている場合は、おそらくそれを使用したいと思うでしょう。 現在からTypescriptがES:Harmony(特にnull以外の可能性があるものと同じくらい普及しているもの)をサポートするまでの間にTypescriptに重大な変更が発生した場合、私はその議論に勝つとは思えません。 すべての開発者を再トレーニングするのにかかる時間とお金は言うまでもありません(私は「Typescriptの担当者」であり、Typescript開発者をトレーニングするのが私の仕事です)。
したがって、私は重大な変更を導入することに本当に反対しています。
古いコードを壊さずに将来のコードにそれらを導入できる方法があれば、null不可能なものを自分で使用できることを気にしません(これが私の提案の理由です)。 最終的にはすべてのコードが変換されるのが理想的ですが、人々のトレーニングやサードパーティのライブラリの使用にかかる時間と費用は大幅に削減されます。

私は、ファイルごとのディレクティブの代わりにシンボルを使用するというアイデアが好きです。なぜなら、シンボルはコンテキストを失うことなく伝播する能力を持っているからです。

@Grifforkわかりました、私があなたの懸念を理解していると言ったように、私はあなたが私のことを理解していることを望みます。
今、私は同じ意見を持っていません、技術は変化し、安定したものは何もありません(Angular 2の周りのドラマ全体がそれを証明しています)、そして私は物事を壊し、私が持っているものに固執するよりも優れたツールを持っていることを好みます、私は長期的には、時間とお金を稼ぐことができます。
先に結論したように、ここでコンセンサスが得られるとは思えません。私は指令を好みます。あなたはあなたの提案を好みます。
とにかく、私が自分の部分で言ったように、コンパイラをフォークしてnull以外の型を追加しようとします。これは、コンパイラの大きな変更ではないと思うからです。
議論してくれてありがとう面白かった

@fdecampredon

議論してくれてありがとう

あなたが啓発を意味していると仮定すると(興味深い作品も:D)、私も議論を楽しんだ:)。
あなたのフォークで頑張ってください、そして私たちはここにTSの人が少なくとも私たちの提案のいくつかを却下することを願っています(それで私たちは彼らが絶対に実装したくないものを知っています)。

したがって、 @ RyanCavanaughが説明した問題に対する私の提案された解決策をさらに--noImplicitNullフラグを追加した後、フラグなしで実行した場合のタイプチェッカーの動作が変更されます。

タイプのオーバーロードに|nullが含まれている場合は常に(指定された例のように):

function fn(x: string): number;
function fn(x: number|null): string;

タイプチェッカーはすべてのオーバーロードを受け取り、リストの最後に追加された、その引数の位置に(暗黙の)nullを含む新しいバリアントを作成します。

function fn(x: string): number;
function fn(x: number|null): string;
// implicit signature added at the end, caused by the first overload
function fn(x: null): number;

これは、厳密な--noImplicitNullバージョンと同じ解釈になります。nullが渡されると、最初に一致するオーバーロードは明示的なnumber|nullます。 事実上、明示的なnullを暗黙的なnullよりも強力にします。

(はい、暗黙の定義の数は、 |nullオーバーロードを持つ引数の数とともに指数関数的に増加することを知っています-他のタイプがない場合、2番目のパスでnullを「許可」するより高速な実装があることを望んでいます一致しますが、この状況はまれであると予想されるため、どちらの方法でも問題になるとは思いません)

このように定式化された変更は、完全に下位互換性があり、オプトインされることを願っています。

残りの動作に関するいくつかのアイデアを次に示します。

nullを割り当てるか、値を初期化しないままにしておくと、フラグが渡されない場合、どのタイプの値でも許可されます。

フラグがオンになっている場合、値がnullでないことを期待する関数または演算子に渡されるまで、値を初期化されていない(割り当てられていない)またはnullのままにしておくことができます。 クラスのメンバーについてはよくわかりません。

  • コンストラクターの終わりまでに初期化する必要があります、または
  • 初期化は、それらを完全に初期化するメンバーメソッドが呼び出されたかどうかをチェックすることで追跡できます。

明示的なnullユニオンは、両方のモードで他のすべてのユニオンと同様に動作します。 共用体タイプを絞り込むためにガードが必要になります。

上記のコメントを_すべて_読んだだけで、それはたくさんあります。 非常に多くのコメントと5か月後、構文とフラグという同じ点についてまだ議論しているのは残念です...

大規模なコードベースでは、コンパイラによる静的nullチェックの値について多くの人が強い議論をしていると思います。 これ以上は何も言いません(ただし+1)。

これまでの主な議論は、構文と、多数の既存のTSコードおよびTSライブラリ定義との互換性の問題についてでした。 null型とフロー構文?string導入することが、最もクリーンで最も自然に見える理由がわかります。 しかし、これはstringの意味を変更するため、既存のコードすべての重大な変更になります。

この重大な変更に対して上記で提案された解決策は、コンパイラフラグ( 'use strict';が先例として引用されました。 TSがJSの過去の過ちを継承したからではなく、それを繰り返す必要があります。

そのため、「nullではない」注釈( string!!string 、...)が唯一の方法であり、ディスカッションで新しい議論を紹介しようと思います。そんなに悪くない。

コード全体に前髪( ! )を付けたいと思う人はいません。 しかし、おそらくそうする必要はありません。 TSはタイピングについて多くのことを推測します。

1.ローカル変数
ローカル変数に前髪の注釈を付けることは期待していません。 nullをローカル変数に割り当てても、TSが完全にアサートできる安全な方法で使用する限り、問題ありません。

var x: string;  // Nullable
x.toString();  // Error: x can be undefined or null
x = "jods";
x.toUpperCase();  // Fine.
notNull(x);  // Fine

型の代わりに初期化子を使用することがよくあります。

var x = "jods";  // x: string (nullable)
notNull(x);  // Fine
x = null;  // Fine
notNull(x);  // Error

2.関数の戻り値
多くの場合、戻り値を指定しません。 TSが型を推測するのと同じように、TSはnull許容かどうかを推測できます。

function() /* : string! */ {
  return "jods";
}

編集:継承と関数変数のために悪い考え、以下の@Grifforkによるコメントを参照してください
イベントタイプを返すと、TSは「nullではない」アノテーションを完全に追加できます。

推測されたnull可能性をオーバーライドする場合(たとえば、メソッドがnullを返さないのに、派生クラスがnullを返す可能性があるため)、型を明示的に指定します。

function f() : string {  // f is nullable although this implementation never returns null.
  return "abc";
}

3.機能パラメータ
タイプを明示的に指定する必要があるのと同じように、nullパラメータを受け入れないかどうかを指定する必要があります。

function (x: string!) { return x.toUpperCase(); } // OK
function (x: string) { return x.toUpperCase(); } // Error

古いコードベースでのコンパイルエラーを回避するには、「暗黙的なものはありません」と同様に、「Null逆参照はエラーです」という新しいフラグが必要です。 _このフラグはコンパイラ分析を変更せず、エラーとして報告されたもののみを変更します!_
追加する前髪はたくさんありますか? 実際の大規模なコードベースの統計を行わずに言うのは難しいです。 オプションのパラメーターはJSで一般的であり、すべてnull可能であることを忘れないでください。したがって、「not null」がデフォルトの場合は、これらのアノテーションが必要になります。
これらの前髪は、発信者がnullを受け入れないことを思い出させるものでもあり、今日のstringについて知っていることと一致しています。

4.クラスフィールド
これは3に似ています。内容を推測できずにフィールドを読み取る必要がある場合は、宣言時にnull不可としてマークする必要があります。

class C {  x: string!; }

function(c: C!) // : string! is inferred
{ return c.x; } // OK, but annotations are required

初期化子の省略形を想像することができます、多分:

class C {
  x! = "jods"; // Note the bang: x is inferred as !string rather than just string.
}

初期化子のデフォルトがnot-nullであると言う場合は、null許容プロパティについても同様の省略形がおそらく望ましいでしょう。
繰り返しになりますが、既存のコードのほとんどのフィールドがnull許容であるかどうかを判断するのは難しいですが、このアノテーションは、開発者が今日持っている期待と非常に一致しています。

5.宣言、 .d.ts
大きな利点は、既存のすべてのライブラリが機能することです。 これらはnull値とnull以外の値を入力として受け入れ、nullの可能性のある値を返すと想定されます。
最初は、いくつかの戻り値を明示的に「null以外」にキャストする必要がありますが、定義がゆっくりと更新されてnullが返されない場合を示すため、状況は改善されます。 パラメータに注釈を付ける方が安全ですが、正常にコンパイルするために必須ではありません。

コンパイラが値を推測できない「nullではない」状態を示すことは有用であると思います(たとえば、型指定されていないコードを操作するとき、または開発者がプロ​​グラムのグローバル状態に関していくつかのアサーションを作成できるとき)。いいかもしれません:

var a: { s: string } = something(); // Notice s is nullable.
notNull(a.s); // Error
notNull(<!>a.s);  // OK, this is a shorthand "not-null" cast, because the dev knows about something().

コンパイラーに(簡単な方法で)何かがnullではないことを示唆する機能が望ましいと思います。 これは、デフォルトではnull以外の場合にも当てはまりますが、単純で論理的な構文を使用するのは困難です。

申し訳ありませんが、これは非常に長い投稿でした。 「デフォルトではnull」であっても、すべてのコードに注釈を付ける必要はないという考えを実証しようとしました。TSは、私たちの助けがなくてもほとんどのコードの正しさを推測できます。 2つの例外は、フィールドとパラメーターです。 これは過度にうるさくはなく、優れたドキュメントになると思います。 このアプローチを支持する長所は、下位互換性があることです。

私はあなたが@ jods4と言ったことのほとんどに同意しますが、いくつか質問と懸念があります:

2)コンパイラがreturn型でnull許容型をnull許容型に自動的に変換できる場合、これをどのようにオーバーライドできますか(たとえば、継承または置き換えられる関数の場合)?

3&5)コンパイラがnull許容から非null許容に変換できる場合、コードファイルの関数シグニチャはd.tsファイルの同じ関数シグニチャとは異なる動作をするようになりました。

6)共用体タイプを使用する場合、強打はどのように機能/外観しますか?

2)良いキャッチ。 継承(または「委任」)では実際にはうまく機能しないと思います。
私がTSで経験したことは、関数からの戻り値を明示的に型指定することはほとんどないということです。 TSはそれらを理解し、TSはヌル/非ヌルの部分を理解できると思います。

それらを明示的に指定する唯一のケースは、拡張性(継承を含む)などのために、インターフェースまたは基本クラスを返したい場合です。 ヌル可能性は、この説明に非常によく適合しているようです。

上記の私の提案を変更しましょう。明示的に入力された戻り値は、宣言されていない場合、null許容ではないと推測されます。 これは、提案するケースに適しています。 これは、子クラスでnullになる可能性のあるコントラクトで「nullではない」と推測するコンパイラーをオーバーライドする方法を提供します。

3&5)上記の変更により、もはやそうではありませんよね? コードと定義の関数署名は、まったく同じように動作するようになりました。

6)興味深い質問です!
これは、特にジェネリックをミックスに追加する場合は、実際には素晴らしいものではありません。 私が考えることができる唯一の方法は、すべての部分がnullでない必要があるということです。 ジェネリックスのために、おそらくnull型リテラルが必要です。私の最後の例を参照してください。

var x: number | string!; // compiler error
var x: number | string; // x can be null
var x: number! | string!; // x cannot be null
function f<T>() : number | T; // f can be null
function f<T>() : number! | T; // f is nullable if T is nullable
function f<T, G>(): T | G; // f is nullable if T or G is nullable
function f<T>(): T | null; // f is nullable even if T is not nullable.
function f<T>(): T!; // Whatever T is, f never returns null.

// Generics constraint option 1
function f<T!>(x: T!, y: T): T!; // T: not nullable type, x: not-null, y: null, f: not-null
function f<T!>(x: T!, y: T): T;  // T: not nullable type, x: not-null, y: null, f: null

// Generics constraint option 2
function f<T!>(x: T, y: T | null): T; // same as option 1.1
function f<T!>(x: T, y: T | null): T | null;  // same as option 1.2

説明のために、デフォルトでnull以外の型を選択した場合は、新しい構文も考案する必要があることに注意してください。ジェネリック型がnull許容値を許可してはならないことをどのように表現するか。
逆に、ジェネリック型Tにnullが含まれている場合でも、関数がTを返すことはなく、nullを返すことはないことをどのように指定しますか?

タイプリテラルは大丈夫ですが、どちらもあまり満足のいくものではありません: var x: { name: string }! 、特に複数の行にまたがる場合:(

その他の例:
null以外の数値の配列number![]
nullではない数値の配列: number[]!
nullにすることはできません: number![]!
ジェネリック: Array<number!>[]

2、3、5)提案の変更に同意しますが、うまくいくようです。
6)

function f<T>() : number | T; // f can be null
function f<T>() : number! | T; // f is nullable if T is nullable

ここでは、fをnullに割り当てることができるのではなく、fがnullを返すことができることを意味していると思います。

組合の場合、次の構文がもう1つの利用可能なオプションであると思いますか? (少しばかげているように見えますが、場合によってはより簡潔でわかりやすいかもしれません)。

var x = ! & number | string;

どちらもnull許容でないことを意味します。


リテラル型の場合、 var x: !{name: string}ように、リテラル式の最初と最後に強打を付けることができます。私の意見では、最後に置くよりも操作が簡単です。

@ jods4 「明示的なnullは暗黙的なnullよりも優先される」という提案された修正について読みましたか? これで、さまざまなセマンティクスの問題が解決されると思います。

@Griffork
はい、もちろん、「null許容」は「nullを返すことができる」という悪い言い回しでした。

これは、 function f() {}が実際にはf宣言であり、後で設定できると思います... fがnullに設定されることは決してないことを表現したいですか? function f!() {} ? 私の意見では、それは行き過ぎのようです。 タイプチェッカーは、これが常に当てはまると想定する必要があると思います。 とにかく処理できない他の珍しいエッジケースがあります。

新しいオプションに関して、私はそれが少しばかげているように見え、型宣言に1つの追加のシンボルを導入することに同意します: & 、単一の目的で...それは私には良い考えのようには見えません。 また、いくつかの言語では、 & AND| ORよりも高い優先度でバインドされますが、これはここでは発生しません。

タイプの前に強打を置くことは可能な代替案です、私たちは何がより良く見えるかを見るためにすべての例を書き直そうとするべきだと思います。 私が提案したこのフィールドショートカットは問題になると思います:

class C {
  name! = "jods";  // Field inferred string, but marked not nullable.
  // Maybe we could do that instead, which works with "!T" convention:
  name : ! = "jods";
  // That kind of make sense with the proposed <!> cast.
}

@spion
はい私はそれを見ました。

まず、それが目前の問題を本当に解決するかどうかはわかりません。 では、どのオーバーロードがfn(null)バインドするか、新しい定義を使用しますか? numberを返す「生成された」もの? これは、2番目のオーバーロードもnull受け入れ、 string返すという事実と矛盾していませんか? それとも、オーバーロードの解決を実行できないため、コンパイラエラーですか?

第二に、私たちは他の多くの問題を思い付くことができると確信しています。 スイッチを押すだけでソースコードのグローバルな意味を変えることはできないと思います。 .d.tsでなく、人々はコードを再利用するのが大好きです(ライブラリの再利用、さらにはコピー貼り付け)。 あなたの提案は、私のソースコードのセマンティックな意味はコンパイラフラグに依存しているということであり、それは私には非常に欠陥のある提案のようです。

しかし、コードがすべての場合に正しくコンパイルおよび実行されることを証明できれば、私はそれですべてです!

@ jods4

どちらの場合も、2番目のオーバーロードが使用されます

function fn(x: string): number;
function fn(x: number|null): string; 

2番目の明示的なnullは、どのフラグが使用されているかに関係なく、暗黙的な(最初のnull)よりも優先されるためです。 したがって、フラグによって意味が変わることはありません。

nullタイプは現在存在しないため、一緒に導入され、変更は完全に下位互換性があります。 古い型の定義は引き続き正常に機能します。

@spion
では、暗黙のfunction fn(x: null): number;の問題は何でしたか? 2番目のオーバーロードが常に優先される場合、どのフラグが使用されても、この「修正」全体はまったく効果がありませんか?

その他の質問:今日、すべてのlib定義には「nullが許可された」動作があります。 したがって、それらが_すべて_更新されるまで、「暗黙的にnull」フラグを使用する必要があります。 このフラグをオンにした状態で、プロジェクトでnull以外のパラメーターを受け取る関数を宣言するにはどうすればよいですか?

// null by default flag turned on because of 3rd party libs.
function (x: string)   // <- how do I declare this not null?
{ return x.toUpperCase(); }

修正は確かに効果があります。 これにより、フラグがある場合とない場合の動作が一貫し、上記のセマンティックの問題が修正されます。

次に、サードパーティのライブラリに「デフォルトでnull」フラグを使用する必要はありません。 この機能の最も重要な点は、ほとんどの場合、メリットを得るために_何もする必要がないということです。

文字列の単語数を計算するライブラリがあるとしましょう。 その宣言は

declare function wordCount(s: string): number;

コードでこの関数を次のように使用するとします。

function sumWordcounts(s1:string, s2:string) {
  return wordCount(s1) + wordCount(s2);
}

このプログラムは、暗黙のnullが無効になっている場合でも、両方のコンパイラで渡されます。 コードは完全に下位互換性があります。

どうして? なぜなら、今日のコンパイラは、値を使用しようとするすべての場所で値がnullではないという信念をすでに持っているからです(理論的にはnullになる可能性がありますが)。

新しいコンパイラは、値を使用しようとしたときに、値がnullではないと想定します。 値がnullになる可能性があることを指定していないため、他の方法で信じる理由はありません。そのため、動作の一部は同じままです。 動作の変更は、nullを割り当てて(または変数を初期化せずに)、それらの値を上記のような関数に渡そうとした場合にのみ有効になります。 通常値を使用するコードの他の部分を変更する必要はありません。

次に、 wordCount実装を見てみましょう。

function wordCount(s) {
  if (s == '') return null;
  return s.split(' ').length
}

おっと、このタイプは全体像を伝えるものではありません。 その関数がnull値を返す可能性があります。

問題はまさにそれです。 たとえ私たちが望んでいたとしても、現在のコンパイラで全体像を伝えることは不可能です。 確かに、戻り値は数値であり、nullになる可能性があることを暗黙的に示しています。ただし、nullになる可能性のある値に誤ってアクセスしようとしても、コンパイラはそのことを警告しません。 それは常にそれが有効な数であると喜んで考えます。

noImplicitNull変更後、同じ型定義を使用すると、ここでまったく同じ結果が得られます。 コードは文句なしにコンパイルされます。 コンパイラは、wordCountが常に数値を返すと喜んで考えます。 以前と同じように、空の文字列を渡すと、プログラムは失敗する可能性があります(古いコンパイラは、返された数値がnullである可能性があることを警告しません。また、新しいコンパイラは型定義を信頼するため、新しい数値も失敗しません)。 また、同じ動作が必要な場合は、コード内で何も変更せずにそれを維持できます。 (1)

ただし、これで、より適切に実行できるようになります。wordCountの改善された型定義を記述できるようになります。

declare function wordCount(s:string):string|null;

nullの戻り値をチェックせずにwordCountを使おうとすると、コンパイラの警告が表示されます。

最良の部分は、この改善が完全にオプションであるということです。 すべての型宣言をそのままにして、今とほぼ同じ動作を得ることができます。 型宣言は悪化しません(2)-必要と思われる場合にのみ、より正確に改善することができます。


(1):型定義を改善しなくても、ここではすでに改善があります。 新しい型定義を使用すると、 wordCount関数がそのnullを.split()しようとしたときに、誤ってnullをsumWordcounts渡してnullポインター例外を取得することはありません。

sumWordcounts(null, 'a'); // error after the change

(2):わかりました、それはうそです。 型宣言は、関数の小さなサブセット(オプションの引数ではないnull許容引数を取る関数)では悪化します。

オプションの引数については、引き続き問題ありません。

declare function f(a: string, b?:string); 

ただし、オプションではなく、nullになる可能性のある引数には使用できません

declare function f(a: string, b:string); // a can actually be null

これらの機能は非常にまれであり、必要な修正は最小限であると私は主張します。

@spion

修正は確かに効果があります。 これにより、フラグがある場合とない場合の動作が一貫します。

どのように、あなたは完全な例をあげることができますか? あなたも言った:

どちらの場合も、2番目のオーバーロードが使用されます

私にとって、これは修正が効果がないことを意味しますか?

このプログラムは、暗黙のnullが無効になっている場合でも、両方のコンパイラで渡されます。 コードは完全に下位互換性があります。

はい、どちらの場合もエラーなしでコンパイルされますが、同じ結果にはなりません。 sumWordCounts()は、1つのケースではnumber!として入力され、2番目のケースではnumber?として入力されます。 スイッチを使用してコードのセマンティクスを変更することは、非常に望ましくありません。 前に示したように、この変更は、オーバーロードの解決など、グローバルな影響を与える可能性があります。

新しいコンパイラは、値を使用しようとしたときに、値がnullではないと想定します。 そうでなければ信じる理由はありません

番号! これが要点です。nullの可能性のある値を使用すると、コンパイラーがエラーをスローするようにします。 私がコーディングするときのようにnullでないことを「想定」している場合、この機能は役に立ちません。

動作の変更は、nullを割り当てて(または変数を初期化せずに)、それらの値を上記のような関数に渡そうとした場合にのみ有効になります。

「上記のような関数」は実際にはnullを受け入れるので、私にはわかりません...

最後のコメントの終わりを読んで、スイッチをオンにするまで実際の静的null分析を取得できないという印象があります。これは、ライブラリ定義が修正されるまで実行できません。 :(

一般的に言って、すべてのケースとすべてが言葉だけでどのように機能するかを完全に理解することは困難です。 いくつかの実用的な例が非常に役立ちます。

例と、コンパイラの動作を次に示します。

nullを返さない関数yes(): string!とnullを返す可能性のあるno(): string?を持つライブラリを想定します。

.d.ts定義は次のとおりです。

declare function yes(): string;
declare function no(): string;

静的にチェックされたnullの恩恵を受け、既存のライブラリ定義を使用する大規模なプロジェクトをTSで作成できるようにしたいと考えています。 さらに、すべてのライブラリが正しいnullness情報で更新される状況に_プログレッシブ_に移行できるようにしたいと思います。

上記の提案されたnullではない修飾子!を使用して、次のことができます。

function x(s: string!) {  // inferred : string, could be explicit if we want to
  return s.length === 0 ? null : s;  // no error here as s is declared not null
}

x(no());  // error: x called with a possibly null parameter
y(<!>yes());  // no error because of not null cast. When .d.ts is updated the cast can be dropped.

それはあなたのアイデアでどのように機能しますか?

このスレッドは非常に長い議論(130コメント!)であり、誰もが言及したアイデア、提案、問題を追跡するのは非常に困難です。

提案された構文、TSで受け入れられるもの、エラーとは何かなど、提案を使用して外部の要点を作成することをお勧めします。

@spionのアイデアにはコンパイラフラグが含まれるため、コードの各部分について、フラグを設定および設定解除してTSの動作を文書化する必要があります。

!T非ヌルマーカーの要点は次のとおりです。
https://gist.github.com/jods4/cb31547f972f8c6bbc8b

上記のコメントとほとんど同じですが、次の違いがあります。

  • 関数パラメーターの「nullではない」側面は、コンパイラーによって安全に推測できることに気づきました(その洞察をます)。
  • 特に戻り値の推測に関する@Grifforkコメントを含め、 T!代わりに!Tを試しました。
  • コンストラクターの動作など、いくつかのセクションを追加しました。

コメント、フォーク、代替案の作成を自由に行ってください( ?T )。
これを前進させてみましょう。

@ jods4

どちらの場合も、2番目のオーバーロードが使用されます

私にとって、これは修正が効果がないことを意味しますか?

  1. これは、両方のフラグで言語セマンティクスを一貫させるために、オーバーロードの解決が変更されたことを意味します。

はい、どちらの場合もエラーなしでコンパイルされますが、同じ結果にはなりません。 sumWordCounts()は数値として入力されます! ある場合と数? 第二に。 スイッチを使用してコードのセマンティクスを変更することは、非常に望ましくありません。 前に示したように、この変更は、オーバーロードの解決など、グローバルな影響を与える可能性があります。

  1. 私の提案にはnumber!はありません。 どちらの場合も、タイプは単純にnumberになります。 これは、null値を除いて、オーバーロード解決が通常どおり機能し続けることを意味します。この場合、明示的なnullの新しい動作は、暗黙的な正規化よりも優先され、下位互換性のある方法でセマンティクスを正規化します。

番号! これが要点です。nullの可能性のある値を使用すると、コンパイラーがエラーをスローするようにします。 私がコーディングするときのようにnullでないことを「想定」している場合、この機能は役に立ちません。

  1. 私が表現しようとしていたのは、(型宣言に関して)機能の後方互換性がほとんどないということです。 使用しない場合は、以前とまったく同じ動作になります。 null型の値が返される可能性を表現するために使用したい場合は、これで可能です。

これは、コンパイラが以前にタイプobjectおよびstring値にstringタイプを使用したかのようであり、すべてのオブジェクトですべての文字列メソッドを許可し、それらをチェックすることはありません。 これで、 Object用に別の型が作成され、代わりにその型の使用を開始して、文字列メソッドが常に使用できるとは限らないことを示すことができます。

「上記のような関数」は実際にはnullを受け入れるので、私にはわかりません...

この関数を見てみましょう:

function sumWordcounts(s1:string, s2:string) {
  return wordCount(s1) + wordCount(s2);
}

新しいコンパイラフラグの下では、null値( sumWordCounts(null, null);など)で呼び出すことはできません。これが唯一の違いです。 wordCountsの定義では、文字列を受け取り、数値を返すと記述されているため、関数自体がコンパイルされます。

最後のコメントの終わりを読んで、スイッチをオンにするまで実際の静的null分析を取得できないという印象があります。これは、すべてのライブラリ定義が修正されるまで実行できません。 :(

コードの大部分は、nullまたは未定義の値を実際には処理しません。ただし、null /未定義かどうかを確認し、エラーをスローして、コードベース全体でその値が完全に無関係な場所に伝播されないようにします。デバッグします。 オプションの引数を使用して、あちこちにいくつかの関数があります。これらは認識可能であり、おそらく新しいスイッチでそれに応じてモデル化できます。 私の例のように関数がnull値を返すことはそれほど頻繁ではありません(これは、代表的なケースとしてではなく、機能についてのポイントを示すためにのみ使用しました)

私が言っているのは、型定義の大部分は正しいままであり、修正が必要な残りのいくつかのケースでは、nullケースが重要でないと見なされた場合、修正しないことを選択できます。気になる場合は、それに応じてタイプを変更してください。 これは、必要なときにいつでもより強力な保証を取得するというtypescriptの目標である「ダイヤルを上げる」という目標と完全に一致しています。


さて、既存の型定義は

declare function yes(): string;
declare function no(): string;

また、機能が追加される前にコードが作成されたとしましょう。

function x(s: string) {
  return s.length === 0 ? null : s;
}

新しいコンパイラでは、動作は古いコンパイラとまったく同じになります

x(no());  // no error
x(yes());  // no error

x()の結果のように、コンパイラがnullである可能性があることを知っていることを試みない限り、

x(x(no())) // error, x(something) may be null

no()を見て、nullを返すケースがまれであると判断して、それをモデル化しないか、型定義を修正することができます。

次に、提案で何が起こるかを見てみましょう。外部ライブラリにさえ触れるコードのすべての行が壊れます。 上記のように完全に有効な型定義を持つ関数も壊れます。 コンパイラが文句を言わないようにするには、すべての引数アノテーションを更新し、 !を追加するか、どこにでもnullチェックを追加する必要があります。

肝心なのは、TypeScriptの型システムが現在間違っているということです。 null値の二重の解釈があります。

タイプT変数にnullを割り当てようとしたり、タイプTが必要な引数としてnullを渡そうとすると、タイプがT|nullます。

タイプT値を使用しようとすると、タイプがTであるかのように動作し、nullの可能性はありません。

あなたの提案は、すべての通常のタイプTを常にT|nullとして扱うことを提案しています。 私はそれらを単なるTとして扱うことを提案しています。 どちらもセマンティクスを変更します。 どちらもコンパイラを「正しく」動作させます

私の主張は、「ちょうどT 」のバリアントは、見た目よりもはるかに苦痛が少なく、コードの大部分と調和しているということです。

編集:あなたの提案は「!」なしでタイプを扱い続けることかもしれないことに気づきましたまた "?" 以前と同じように。 これは下位互換性がありますが、メリットを得るには多くの作業が必要です(コードの大部分は、チェック/スロー以外のnull値を処理しないためです。

@spion

1.これは、両方のフラグで言語セマンティクスを一貫させるために、オーバーロードの解決が変更されたことを意味します。

あなたは同じ事実を述べているだけですが、それがどのケースにどのように対処するのかはまだ私にはわかりません。
だから具体的な例をお願いしました。

1.私の提案にはnumber!がありません。 どちらの場合も、タイプは単純にnumberになります。

それは正しくありません。構文は異なりますが、基本的な概念は同じです。 私がnumber!と言うとき、私はnull以外の数値タイプを強調しています。これは、あなたの提案では単にnumberます。 そして、2番目のケースはあなたのケースではnumberではなく、 number | nullます。

コードの大部分は、nullまたは未定義の値を実際には処理しません。ただし、null /未定義かどうかを確認し、エラーをスローして、コードベース全体でその値が完全に無関係な場所に伝播されないようにします。デバッグします。 オプションの引数を使用して、あちこちにいくつかの関数があります。これらは認識可能であり、おそらく新しいスイッチでそれに応じてモデル化できます。 私の例のように関数がnull値を返すことはそれほど頻繁ではありません(これは、代表的なケースとしてではなく、機能についてのポイントを示すためにのみ使用しました)

このような記述は、多くの仮定とショートカットを作成すると思います。 そのため、進歩を遂げるには、コード、構文、およびシステムの動作の説明を含む、より具体的な例に進む必要があると思います。 あなたは言う:

コードの大部分は、nullまたは未定義の値であるかどうかをチェックしてエラーをスローする以外は、実際にはnullまたは未定義の値を処理しません。

非常に議論の余地があります。

オプションの引数を使用して、あちこちにいくつかの関数があります。

さらに議論の余地があります。 JSライブラリにはそのような例がたくさんあります。

、認識可能であり、おそらく新しいスイッチでそれに応じてモデル化することができます

これは大きな近道なので、より具体的な提案をしてください。 これが実際のJSコードのどこに向かっているのかはわかると思いますが、 declare function(x: {});内またはinterface内のオプションの引数をどのように「認識」しますか?

私の例のように、関数がnull値を返すことはそれほど頻繁ではありません。

再び非常に議論の余地があります。 多くの関数はnullまたはundefined値を返します: find (アイテムが見つからない場合)、 getError (エラーがない場合)など。 ...さらに多くの例が必要な場合は、標準のブラウザAPIを見るだけで、_plenty_が見つかります。

私が言っているのは、型定義の大部分は正しいままであるということです。

以前のコメントからわかるように、私はそれを確信していません。

修正が必要な残りのいくつかのケースでは、nullのケースが重要でないと判断した場合は修正しないか、必要に応じてタイプを変更することを選択できます。 これは、必要なときにいつでもより強力な保証を取得するというtypescriptの目標である「ダイヤルを上げる」という目標と完全に一致しています。

この時点で、このステートメントは私には些細なことではないようです。 それがどのように機能するかについて具体的な例を挙げていただけますか? 特に_progressively_の部分。

次に、提案で何が起こるかを見てみましょう。外部ライブラリにさえ触れるコードのすべての行が壊れます。 上記のように完全に有効な型定義を持つ関数も壊れます。 どこでもすべての引数アノテーションを更新し、 !を追加して、コンパイラが文句を言わないようにするか、どこでもnullチェックを追加する必要があります。

これはほぼ完全に間違っています。 私が書いた要点を注意深く読んでください。 実際には、null以外の注釈がほとんど必要ないことに気付くでしょう。 しかし、確かに、重大な変化が1つあります。

ライブラリ定義を使用する既存のコードベースを使用し、コードを変更せずに私の提案でコンパイルしようとするとします。

  • 注釈がなくても、多くのnull分析の利点が得られます(Flowコンパイラで得られるのとほぼ同じ方法です)。
  • 壊れることが1つあります。それは、nullを返さないことがわかっているライブラリ関数の戻り値を使用することです。
declare function f(): string; // existing declaration, but f will never return null.
var x = f();
x.toUpperCase();  // error, possibly null reference.

長期的な修正は、ライブラリ定義をより正確になるように更新することです: declare function f(): !string;
短期的な修正は、null以外のキャストを追加することです: var x = <!>f();
また、依存関係の多い大規模なプロジェクトをより簡単にアップグレードできるように、「暗黙的なものはありません」のようなコンパイラフラグを追加することをお勧めします。 これは、新しいコンパイラを使用して、ライブラリが定義を更新するまで待つことができることを意味します。 注:提案とは異なり、このフラグはコンパイラのセマンティクスを変更しません。 エラーとして報告されたものだけを変更します。

私が言ったように、私たちはこの議論で進歩を遂げているかどうかはわかりません。 私の要点を見て、フラグの両方の状態で独自のアイデア、コード例、および動作を使用して要点を作成することをお勧めします。 私たちは皆コーダーなので、もっと明確になると思います。 また、考慮すべきエッジケースがたくさんあります。 そうしたら、特別な状況を尋ねる質問がたくさんありますが、正確な定義なしに概念やアイデアについて議論することは実際には無駄です。

この130以上のコメントの問題では、さまざまなことが起こっていることについての議論が多すぎます。

ここで_一般的な_議論を続けることをお勧めします。

TSでnull以外のタイプを実装する可能性のある具体的な提案の議論については、それぞれが単一の設計提案に関する新しい問題を開くことをお勧めします。 !T構文について説明するために#1800を作成しました。

@spionデザインにも問題を作成することをお勧めします。

多くの人がnull以外のタイプを望んでいます。 新しい言語(Rustなど)では簡単ですが、既存の言語でレトロフィットするのは非常に困難です(人々は長い間、.netでnull以外の参照を求めてきました-そして私たちが得ることはないと思います彼ら)。 ここのコメントを見ると、それは些細な問題ではないことがわかります。
フローは、これがTSで実行できることを確信しています。それを実現してみましょう!

@ jods4申し訳ありませんが、あなたの提案はnull以外の型とは何の関係もありません。それは、ある種の制御フロー分析に関連していて、ほとんど予測できず、下位互換性を完全に壊します(実際、有効です1.4tsは、要点に記載されているルールの下で失敗します)。
私は、議論がどこにも行かず、130のコメント+が多分多すぎるという事実に同意します。 しかし、おそらくそれは、ここで議論している人々の2つのグループがあるためです:

  • null以外の型をデフォルトにする必要があると考え、それを実現する方法を見つけたい人(フラグまたはその他のメカニズムを使用)
  • null以外の型を望まず、単にそれらの導入を避けようとするか、新しい構文でそれらをプッシュしようとする人。

この2つのグループは、長い間お互いの議論を聞いていたのでやめました。結局、必要なのはTSチームの視点です。それまでは、個人的にこのテーマについて話し合うのをやめます。

@fdecampredon
私の要点については、#1800にあなたの議論をコピーして貼り付けました。本当に興味があれば、なぜそれが悪いデザインなのかを話し合うことができます。 私はあなたの見方を本当に理解していませんが、ここで議論を始めたくありません。それが私が最初に#1800を作成した理由です。

このスレッドに関して、私はあなたに同意します。議論はどこにも行きません...

以前のコメントからわかるように、私はそれを確信していません。

さて、私は他の方法でそれを説明する方法を本当に知りません-私はすでにそれを数回説明しました。 もう一度試してみましょう。

今、あなたはそれを表現することはできません

declare function err():string;

nullを返します。 typescriptはいつでも喜んでerr().split(' ')を実行できるので、それは不可能です。 「それは有効ではないかもしれない」と言うことはできません。

この変更後、 string?stringまたはstring|null変更することで可能になります

しかし、そうでない場合は、変更前に持っていた_何も失うことはありません_:

変更前はnullチェックがなく、変更後もありません(何もしなければ)。 これはほとんど下位互換性があると私は主張しました。 もちろん、より大きなコードベースでデモンストレーションする必要があります。

違いは次のとおりです。変更前にnullチェックを強制することはできませんでしたが、変更後には_できます_(徐々に、最初に最も気になる定義で、次に他の場所で)

@fdecampredonこの問題について、またこの機能を利用することがどれほど「難しい」か「厳格」かについて多くの誤解があると感じているので、私はまだこのテーマについて話し合っています。 潜在的な問題の多くはひどく誇張されているのではないかと思います。 おそらく、フォークに--noImplicitNull基本的なバリアントを実装する必要があります。この次の期間の時間を見つけることができるかどうかはわかりませんが、そうする場合は試してみます。 、楽しいプロジェクトのように聞こえます。

@spion 、私はすべてnullキーワードで宣言されているnullにそれとあなたの提案で定義されていないものとの間の動作は何ですか?

そして、 @ fdecampredonの唯一の要求は、大きな

現在の例では@spionを推測していますが、割り当てられていないローカル変数がnull許容型の関数パラメーターに変換されると仮定すると、現在と同じ方法で

@spion
あなたの最後のコメントから私が理解していることは、私が以前に理解したこととは大きく異なります...

したがって、 : stringは、「 <any>が型の正しさをチェックされないように、nullがチェックされない古い文字列型」です。

新しい構文: string | nullは、以前は存在しなかったため、下位互換性があり、nullチェックなしでこの型にアクセスするとエラーになります。

それは興味深いことであり、すべての影響を把握するのは困難です。 しばらく考えなければなりません。

頭に浮かぶ最初の質問は、入力値(関数パラメーターなど)にどのように制約を課すかです。

declare f(x: string): void;
f(null);

これは大丈夫ですか、それともエラーですか? よろしければ、どうすればエラーにすることができますか。

_私はまだこの議論の中のアイデアが失われていると思います、あなたはあなたのこのアイデアで新しい問題を開きたくないですか? そこで話し合うことができます。_

エラーになる@jods

declare f(x: string): void; //string must not be null.
declare f(x: string|null): void; //string may be null (not sure about undefined here).
declare f(x?: string): void; //I assume x may be null or undefined.

@ jods4 1つのトピックについて複数の問題を作成する必要はないと思います。何かが起こったかどうかを確認するためだけに、10の異なる提案に移動する必要があるため、追跡が難しくなります。 。 そして、それらはすべて、紹介の中で他の提案へのリンクを持っている必要があります。そうすれば、非ヌル可能性を探しに行くすべての人が、1つだけを見て投票するだけではありません。

@fdecampredonたくさんの投稿があり、未解決のものがたくさんあることは知っていますが、それは誰もが好きな解決策を見つけるのをやめるべきだという意味ではありません。 私たちがこれについて話し、自分自身を説明することを喜んでいるという事実が気に入らない場合は、トピックから退会することを歓迎します。

@Griffork
しかし、魔法の旗をつけて初めてですよね?
したがって、フラグをオフにすると、以前は存在しなかった新しいnullablesタイプを除いて、チェックされていないコードがあります。 次に、フラグをオンにすると、すべてのタイプがnull許容ではなく、チェックされますか?

最終結果はおそらく最良の解決策だと思います。 _しかし、今日存在するほとんどすべてのコードが壊れています。_300K行のコードがあると想像してください...フラグをオンにする前に、型が実際にnull許容であるすべての場所にnull許容アノテーションを追加する必要があります。 それは大したことです。

私が正しく理解していれば、これは新しいプロジェクト( .d.tsが更新された後)には適していますが、既存のコードには非常に苦痛です。

@Grifforkこの単一の問題とその長いコメントのスレッド

@spionの当初の提案には魔法の旗はありませんでした。 これは、typescriptの動作方法に対する主要な変更でした。

はい、しかし、この議論に参加する人は、その前に来たすべてを読むべきです。 人々が以前に来たすべてを読むべきではないと思うなら(そしてそれ故に同じ提案を提案する可能性がある)_それなら_私たちはそれを分割することができます。 しかし、私は同意しません。 1つの場所にあることのポイントは、さまざまな詳細やさまざまな可能な実装について話していたにもかかわらず、すべて同じことについて話していたということです。
同じトピックであり、異なるトピックではありません。
すべての会話は一元化されており、応答されないことによって会話が埋もれてしまうことはありません。

多くの提案が現在議論されている多くの異なる詳細に触れることが多いため、この会話を実装の詳細で分類することもできません。

提案を互いに分割するということは、誰かが欠陥を見つけた場合、複数の異なるスレッドに移動してそれを作成する必要があることを意味します。 その時、人々はお互いの過ちから学んでおらず、人々は(彼らがそうするであろう)ある議論に身を隠し、他の議論で概説された過ちを繰り返す能力を持っています。

重要なのは、会話を分割すると、他のすべてのリンクされた投稿のすべてを読むのが面倒な視聴者のために、人々は多くの投稿を繰り返す必要があります(関連するすべての投稿が他のすべての投稿にリンクしていると仮定します) )。

また、 @ jods4は、何かにnullを割り当てた場合の

Imoは、提案よりも大幅な変更が少なく、nullを割り当てられるべきではないすべての定義を使用して、null不可能な型の利点を得る前に、チェック/タッチする必要があります。

これは、 @ jods4@spionの提案に対する「アップグレード」プロセスの例です(必要なフラグがオンになっていると仮定します)。

function a(arg1: string): string;
a("mystr").toLowerCase(); //errors in <strong i="11">@jods4</strong>'s proposal because a may return null.
a("mystr").toLowerCase(); //fine in <strong i="12">@spion</strong>'s proposal.

a(null).toLowerCase(); //fine in <strong i="13">@jods4</strong>'s proposal because a may accept null.
a(null).toLowerCase(); //errors in <strong i="14">@spion</strong>'s proposal since a doesn't accept null.

最終的には、どちらも大きな変化をもたらしています。 どちらもデバッグが面倒で、変数宣言ではなく変数の使用時にエラーが発生します。 違いは、nullが何かに割り当てられている場合(@spion)のエラーと、nullが何かに割り当てられていない場合(@ jods4)のエラーです。 個人的には、物事にnullを割り当てない場合よりも、物事にnullを割り当てる頻度がはるかに少ないため、 @ spionの変更はあまりありません。

私が好きな他のもの:

  • 変数の型は動的に変化しません。 フローを使用したい場合は、次のようにします。
var s: string;
s.toUpperCase(); // error
var t = (s || "test").toUpperCase(); // ok. Inferred t: string
yes(t);  // ok
t = no();
yes(t);  // error

個人的には、暗黙的なタイプの変更ではなく、no()を呼び出した場合にエラーが発生したいと思います。

  • 記号ではなくnullという単語を含むNullablesは、読みやすく、覚えにくいです。 本当 ! 「not-null」ではなく「not」に使用する必要があります。 頭字語が嫌いなのと同じように、記号が何をするのかを覚えておくのは嫌いです。頭字語を思い出せないので、作業速度が大幅に低下します。

@ jods4現在の会話は人々が追いつくのが難しいことを私は知っていますが、それを別々の問題に分割することは役に立たないでしょう。無知は私たちがしたのと同じ質問をします。 それは実際には会話を_助け_するつもりはありません、それは維持し、追いつくのを難しくするだけです。

@Griffork正直に全部読んだのですが、多くの人が読むとは思えません。 それは多くの異なった無関係なサブディスカッションに入り、私は何度も道に迷いました。 ただし、このスレッドをこれまでよりも重くしないで、そのままにしておきましょう。

null許容型のものがあるたびに、これは重大な変更になります(つまり、ある時点でnullが割り当てられることを意味します。そうでない場合、実際にはnull許容型にはなりません)。 nullが関数に割り当てられた/渡された場合にエラーが報告されますが、修正は宣言時に行われます。 これには、関数宣言、変数宣言、クラス内のフィールド宣言が含まれます...多くのものをレビューして変更する必要があります。

@ jods4未実現のサブディスカッションは、実際のコア情報ではなく、分岐している必要があります。 しかし、今すぐ戻ってそれを変更するには遅すぎます。

@spion 「-nonImplicitNull」フラグを使用した@RyanCavanaugh問題のより簡単な解決策があると思います。アイデアは非常に単純で、 ?type type|nullまたはtype|undefined許可する必要はありません。このフラグなしの

@fdecampredonの場合、利用可能で最新の状態に保たれているすべてのライブラリの2つのバージョンが必要です。
また、コードを試したり、より厳密なプロジェクトのコードに依存するテストを実行したりするための非厳密なプロジェクトが1つあるプロセスも強制終了します。

@ jods4

declare f(x: string): void;
f(null);

提案された--noImplicitNullフラグを使用する場合、はい、それはエラーになります。

私はまだ公式の提案を開くことに抵抗があります。少なくとも、もう少し思考実験を行うか、より単純なバージョンのアイデアをフォークに実装してみたいと思います。

!T提案は、実際には多くのコードも壊してしまうことに気づきました。 既存のタイプのセマンティクスが変更されたためではなく、新しい分析によって多くの場所でエラーが発生するためです。

だから私はそれを閉じました。 私が他のアプローチを好む多くのコードを壊すなら、それはよりきれいに見えます。

私は今、多くの既存のコードを壊さずにこの機能を導入する方法はないと確信しています。 これはまだ書かれていないすべてのコードにとってはまだ良いことであり、おそらく古いコードはコンパイラによるnullチェックの利点なしにコンパイルを続けることができます-これはまさに今日のようです。

公式のTSチームのスタンスは、これとその利点の破壊的な側面に関してどのようなものか疑問に思います。

「デフォルトですべてをnull不可にする」ほどの大きな休憩を取ることはほとんどありません。 明らかにバグである場合にのみ新しいエラーを発行する提案があった場合、それはテーブルにある可能性がありますが、そのような提案は:wink:では実現できません。

影響の範囲が小さかった場合-たとえば、引数としてnullを渡すときの過負荷解決の変更。正確な動作が必ずしもすぐに明らかになるとは限らない場合、それは別の話になる可能性があります。

:+1:正気の人が検出されました: @RyanCavanaugh

物事が機能する既存の方法に変更はありませんが、ユニオンにnull型を導入すると、変数を使用する前に(またはnull以外の型の変数に割り当てる前に)変数を保護する必要がありますか? デフォルトの動作では、通常どおりに入力された変数とnullで入力された変数にnullを割り当てることができますが、正しくマークアップされた場合にnullを返す可能性のある関数はキャストを強制します。

このように重大な変更はありませんが、優れたプラクティスを使用すると、推測されたタイピングと優れたコーディング手法の両方を使用して、タイピングシステムによって多数のバグを検出できます。

次に(後で?)-noImplicitNullCastフラグを追加して、入力の一部としてnullがない変数にnullが割り当てられないようにすることができます(これは、フラグを有効にした@spionの提案のように

これは、すべての通常の入力と--noImplicitAnyフラグの追加とそれほど異ならないだろうと思います。

@Griffork
パッケージの前半( nullタイプを追加し、ガードなしで間接参照を禁止する)は、100%互換性がありません。 思ったより多くのコードを壊してしまいます。 このことを考慮:

この新機能により、組み込みのものを含む多くのlibdefが更新されます。 いくつかの署名を変更して、可能な戻り値として明示的にnullを含めるとしましょう。 いくつかの例:

interface Array<T> {
  find(predicate: (T) => bool) : T | null;
  pop() : T | null;
}
interface Storage {
  getItem(key: string) : any | null;
}

そのようなAPIはたくさんあります。述語に一致する配列要素がない場合はfindundefined pop返し、配列が空の場合はundefined返します。 localStorage.getItemキーが見つからなかった場合、 sessionStorage.getItem両方ともnull返します。

次のコードは正当であり、以前は完全にコンパイルされていました。 エラーで壊れます:

var xs: string[];
if (xs.length > 0) return xs.pop().trim();  // error xs.pop() may be undefined (false positive)

var items : { id: number }[];
var selectedId : number;
// Assume we are sure selectedId is amongst items
var selectedItem = items.find(x => x.id === selectedId);
selectedItem.toString(); // error selectedItem may be undefined (false positive)

そこにあることがわかっているlocalStorageから何かをフェッチする場合も同じ考えです。 そのようなコードはたくさんあり、コンパイルしてコンパイラーに伝えるにはキャスト(またはいくつかの新しい構文)が必要です。これがnullではないと仮定すると、私は何をしているのかわかります。

確かに、これにはいくつかの実際のバグが含まれます。 しかし、これには多くの誤検知も含まれ、これらは重大な変更です。 100K以上のLOCでは、これは簡単なコンパイラのアップグレードではありません。

これはおそらく、機能する唯一のアプローチが次のとおりであることを意味します。最初から作成された新しいコードまたは移行された古いコードは、ヌルチェックを最大限に活用します。 レガシーコードにはまったくメリットがありません。 これは、コンパイラオプション(--enableNullAnalysis)によって駆動され、「遷移」または「プログレッシブ」移行パスはありません。

はい、そうです。 ただし、プロジェクトのlib.d.tsをいつでもフリーズしたり、古いプロジェクトを取得したりできます。
新しいタイピングは古いコードを壊します、それはとにかく起こります。 導入されたときのほとんどすべての新しいタイピングでは、正しく機能するために古いコードを何らかの方法で更新する必要があります(ユニオン、ジェネリックなど)。
この変更は、更新を無視したり、ライブラリ/コード定義をフリーズしたりする人が苦しむことはないことを意味します。
理論的には、これらの関数を使用するほとんどのコードは、とにかく保護する必要があります(通常、大規模なコードベースにあります)。保護しないと、コードに潜んでいるバグがあるためです。 したがって、この変更は、広範囲にわたる重大な変更を引き起こすよりも、エラーをキャッチする可能性が高くなります。

編集
@ jods4この変更を行うことで検出しようとしているエラーを引き起こす可能性のある種類のコードである

編集2
あなたは_some_(間違った/危険な)コードを壊していない場合は、今までにこのトピックに関係するすべての変更を行うことにも意味がありません。

しかし、繰り返しになりますが、これは単なる構文上の問題であるため、すべてが適切にコンパイルされ、あらゆる場所でエラーが発生します。

_新しい入力は古いコードを壊します、それはとにかく起こります。_

これはそうではありません。 例外的な状況を除いて、既存のコードを壊すような新機能を追加することはありません。 ジェネリックスと共用体は、lib.d.tsのコンシューマーが新しいバージョンのコンパイラーを使用するためにコードベースを更新する必要があるような方法で、lib.d.tsに追加されませんでした。 はい、人々は古いバージョンのライブラリを使用することを選択できますが、既存のコードの多くを壊すような言語変更を行うわけではありません(基本的にすべての主要言語の場合のように)。 これには時折例外がありますが(https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes)、それらはごくわずかであり、ほとんどの場合、私たちが壊しているコードはバグ。

ただし、プロジェクトのlib.d.tsをいつでもフリーズしたり、古いプロジェクトを取得したりできます。

lib.d.ts(および私がBTWを使用する可能性のある他の.d.ts)をフリーズすると、非常に強い影響があります。 これは、使用しているライブラリまたは新しいHTMLAPIの更新を取得できないことを意味します。 これは軽視するものではありません。

理論的には、これらの関数を使用するほとんどのコードは、とにかく保護する必要があります(通常、大規模なコードベースにあります)。保護しないと、コードに潜んでいるバグがあるためです。

JSには、他の言語でエラーが発生する多くの場合にundefined (または場合によってはnull )を返すという伝統があります。 この例は、 Array.prototype.popです。 C#では、空のスタックからポップするとスローされます。 したがって、常に有効な値を返すと言えます。 Javascriptで空の配列をポップすると、 undefinedが返されます。 あなたの型システムがそれについて厳格であるならば、あなたはそれをなんとかして世話をしなければなりません。

私はあなたがそれに答えようとしていることを知っていたので、私は合法で機能するコードの例を書きました。 大規模なコードベースでは、APIが特定の状況でnullを返す場合がある例がたくさんありますが、特定のケースでは安全であることがわかっています(したがって、安全性チェックは無視します)。

私はちょうど1時間前にいくつかのライブラリのマイクロタスク部分を見ていました。 主な部分は基本的にこれに帰着します:

class MicroTasks {
  queue: Array<() => void>;

  flushQueue() {
    while (queue.length > 0) {
      let task = queue.pop();
      task();  // error possible null dereference (not!)
     }
  }
}

このようなケースはたくさんあります。

編集2に関して:はい、うまくいけば、この変更はバグをキャッチし、無効なコードを指摘します。 私はそれが有用であると確信しているので、それをどうにかして実現させたいのです。 ただし、TSチームは、_valid_コードの重大な変更を検討します。 ここで決定する必要のあるトレードオフがあります。

@danquirk Fair、それならこの機能は実装できないと思います。
@ jods4あなたの言っていることは理解できますが、そのコードを壊さずにnullableを実装することはできません。

悲しい、私は最終的にその問題を閉じる必要がありますか?

多分。

null許容型と非null許容型の引数を避ければ...

この問題は、一連の新機能を使用して、邪魔にならない方法で対処(または軽減)できます。

  • null-typeguardシンボル?<type>紹介します
    タイプにこの記号の注釈が付けられている場合、そのタイプに直接アクセスするのはエラーです。

`` `TypeScript
var foo:?string;

foo.indexOf( 's'); // エラー
foo && foo.indexOf( 's'); // わかった
`` `

  • フラグを導入--nouseofunassignedlocalvar

`` `TypeScript
var foo:文字列;

foo.indexOf( 's'); // エラー

foo = 'bar';

foo.indexOf( 's'); // わかった
`` `

興味深い歴史的メモ:これに関連する問題を見つけようとしているときに、2013年2月に--cflowuコンパイラオプションについて言及しているcodeplexの古い問題に遭遇しました。 @RyanCavanaugh 、その旗に何が起こったのだろうか?

  • 安全なナビゲーションオペレーター#16を紹介します

TypeScript var x = { y: { z: null, q: undefined } }; console.log(x?.y?.z?.foo); // Should print 'null'

組み合わせて、これらの機能は、実際にすべての人に神経衰弱を引き起こすことなく、nullの使用に関するより多くのエラーをトラップするのに役立ちます。

@NoelAbrahams
あなたの最初の提案は基本的に私の最後の提案とまったく同じです、あなたはただ? | nullの代わりに( lib.d.tsの更新と変更の中断に関する問題に関する

2番目の提案には、 @ RyanCavanaugh (上記)によって以前に対処された問題があります。

言語のセマンティクスを変更するフラグは危険なものです。 1つの問題は、影響が潜在的に非常に非局所的であるということです。
...[をちょきちょきと切る]...
唯一安全な方法は、割り当て可能性のセマンティクスを同じに保ち、エラーとフラグに依存しないものを変更することです。これは、今日のnoImplicitAnyの動作とよく似ています。

異質ではない旗が以前に提案され、 @ RyanCavanaughのコメントのために放棄されたと私はかなり確信しています。

3番目の提案は、現在のトピックとはほとんど関係がありません(入力やコンパイル時のエラーではなく、実行時のエラーをキャッチすることです)。 私がこれを言う理由は、このトピックが作成された理由は、新しいnullを追加するのではなく、「安全」であることがわかっている変数に対して未定義またはnullチェックを実行する必要性をためです(それを追跡する簡単な方法で)またはどこでも未定義のチェック。

提案された提案の1つを実装し、nullables(および他のlibs)を使用するようにlib.d.tsを更新しないことは可能でしょうか? 次に、コミュニティは、必要に応じてnullablesを使用して独自のバージョンを維持/使用できますか?

編集:
具体的には、すべてのライブラリに最新の情報が含まれていますが、タイプガードを必要とするように入力が更新されていません。

@RyanCavanaugh戻ってきて、それほど大きな変更ではないことを示すパッチがある場合(特に.d.tsファイルが更新されていない場合)について話し合います。

@danquirkタイプ定義を更新する必要はありません。 それらは「間違った」ままである可​​能性があり、ほとんどの場合下位互換性があります。

とにかく、この問題は失われた原因のようです。

ちなみに、とりわけこれを行ったMSRによる修正されたコンパイラがありました-彼らの発見とともに利用可能な論文はありますか? 編集:それを見つけました: http

@Griffork 、はい、ほとんどすべてが

どこにでも新しいnullまたは未定義のチェックを追加しない

割り当てられていないローカル変数のトラップはこれを軽減し、安全なナビゲーションオペレーターは提案されたES機能であるため、ある時点でTSに到達します。

また、これがTSに入る可能性は低いと思います... :(

私が考えることができる唯一の解決策は、nullの安全性をオプトインコンパイラフラグにすることです。 たとえば、新しいT | nullまたは?T構文を導入しますが、プロジェクトがオプトインしない限り、エラーを発生させないでください。 そのため、新しいプロジェクトにはメリットがあり、コードを新しい機能と互換性のあるものにすることを選択した古いプロジェクトにもメリットがあります。 大きすぎて簡単に適応できない大きなコードベースは、この機能を利用できません。

そうは言っても、この機能を飛ばすにはいくつかの問題が残っています...

@NoelAbrahams申し訳ありませんが、私の
(/ if)それがネイティブになったとき、私は確かにその機能を使用するでしょう、それは本当にいいですね。

上で引用した@RyanCavanaughコメントと同じ問題がある@ jods4 (フラグが変更されたときの他の場所での問題による割り当てエラー)。

繰り返しますが、最も簡単な方法は、他の提案の1つ(おそらく@ spion 's)を実装し、新しいタイプを.d.ts 'に追加しないことです。

@Griffork
必ずしもそうとは限りませんが、これは残っている「いくつかの問題」の1つです。 この機能は、フラグを有効にしてもプログラムのセマンティクスが変更されないように設計する必要があります。 エラーが発生するだけです。

たとえば、「nullではない」 !Tデザインを使用する場合、この問題は発生しないと思います。 しかし、これは解決の問題にはほど遠いです。

T | nullは重大な変更ですが、 ?Tはそうではないことも指摘しておく必要があります。

@ jods4私は、「セマンティクスの変更」には「割り当て能力の変更」が含まれているという印象を受けました。いずれにしても、非ローカル効果に関する私の(そしておそらく@RyanCavanaughの)懸念は依然として残っています。

@NoelAbrahamsどうやって?
T | nullにはタイプガードが必要です。?Tにはタイプガードが必要です。これらは同じもので、名前や記号が異なります。
はい、フラグを使用して両方を「オン」にすることができます。
違いがわかりませんか?

@Griffork 、私が見るところ?T単に「nullをチェックせずにアクセスしない」という意味です。 チェックを実施するために、この注釈を新しいコードに自由に追加できます。 私はこれを型注釈ではなく一種の演算子として考えています。

アノテーションT|nullは、型がデフォルトでnull許容かどうかについてのステートメントを作成するため、既存のコードを壊します。

@jbondc
おもしろい…まだあなたの提案を深く調べていませんが、そうなると思います。

現時点では、コンパイラによって強制される不変性は、スレッドモデルが共有データを禁止しているため、JSでは他の言語ほど興味深いものではありません。 しかし、確かに覚えておくべきことがあります。

@NoelAbrahams
すべてのnull強制提案は、重大な変更です。 あなたが説明した?Tですら。 上記のいくつかのコメントの理由をいくつか例を示しました。

そのため、これが必要な場合の唯一の方法は、コンパイラオプションにすることと、オプションを有効にすると報告されたエラーは変更されるがプログラムの動作は変更されないように十分に検討することだと思います。 それが実行可能かどうかわからない。

私は実際にこれでちょっと大丈夫です。 TS 1.5を使用すると、必要なものを取得できるようになります。

function isVoid(item:any): item is void { return item == null; }
declare externalUnsafeFunction(...):string|void

function test() {
  var res = externalUnsafeFunction(...);
  var words = res.split(' '); // error
  if (!isVoid(res)) {
    var words = res.split(' '); // ok
  }
}

これで、 isVoidチェックは、 externalUnsafeFunctionまたはその他の関数または関数定義の戻り値に強制されます。ここで|voidを戻り型に追加します。

null / undefinedを受け入れない関数を宣言することはできませんが、ローカル変数とクラスメンバーを初期化することを覚えておくのに十分な注意を払うことは可能です。

@NoelAbrahamsは、null型であっても、将来のコンパイラフラグで変更されない限り、nullを他の型に暗黙的にキャストできるようにする必要があることをすでに説明しました。

また、将来的に、関数がnullを受け入れることができる場所とタイミングに注釈を付けるコンパイラフラグを取り込む

個人的には、代わりに単語を使用できるのに、記号を使用して型を表すというアイデアは嫌いです。

@spionそれは実際には良い点です。 それが現在TSで機能する場合、おそらくすべての組み込みd.tsファイルはすでに「間違っている」ので、提案されたようにnullタイプを追加しても、提案された変更は何も変わりません。

実は、もう少しは達成できるので、今週から上司に使い始めることを提案します。

T|voidを、将来壊れることを恐れる構文とは決して見なさないことに注意します。 それはほとんどナンセンスです。

@RyanCavanaugh void === undefined(割り当て可能な値)と同じくらいナンセンスです。

はぁ。

私たちのコードは大きすぎてT|void依存して開始できません。コードがすぐに壊れて、置き換えられない場合です。 そして、もしそれが壊れるかもしれないなら、私は他のプログラマーにそれを使うように説得することができないでしょう。

わかりました。@ spionでパッチを作成したことがある場合は、お知らせください。私の作業のコードベースに対して実行します。 少なくとも、それが引き起こすエラーの数に関する統計を与えることができます。

@RyanCavanaughナンセンス、本当に? どのように? そして、null / undefinedチェックの前に、あらゆる種類の消費を禁止する必要があるnull許容型を表現するために何を提案しますか?

私は本当にそれから恩恵を受けるであろうたくさんの図書館を持っています。

@Grifforkとにかく、1.5より前のユニオンからvoidを安全に削除することはできません。ユーザー定義の型ガードがない場合はそうではないため、これを使用できるのは1.5以降のみです(可能になった場合)。

このコードを書くのはナンセンスですか?

var foo: void;
var bar: void = doStuff();

もちろん違います。 では、可能な型の和集合にvoidを追加することの意味は何でしょうか。 The Void Type (3.2.4)を説明するセクションにかなり長い間存在していた言語仕様のこの部分に注意してください。

_注:Void型の変数は有用な目的を果たさないため、宣言を禁止することを検討する場合があります。 ただし、Voidはジェネリック型または関数の型引数として許可されているため、Voidのプロパティまたはパラメーターを禁止することはできません。_

この質問:

_そして、null / undefinedチェックの前に、あらゆる種類の消費を禁止する必要があるnull許容型を表現するために何を提案しますか?_

スレッド全体が何であるかです。 Ryanは、 string|voidは現在の言語セマンティクスによればナンセンスであると指摘しているだけなので、ナンセンスセマンティクスに依存することは必ずしも合理的ではありません。

私が上で書いたコードはafaik完全に有効なTS1.5ですよね?

確かにナンセンスではない例を挙げましょう。 LinqのSingle()に似たget()を呼び出す(nodejs)データベースライブラリ関数がありますが、残念ながら、アイテムが見つからない場合はエラー(promiseの拒否)をスローする代わりにPromise<null>返します。 コードベース全体の何千もの場所で使用されており、置き換えられたり、すぐになくなることはないでしょう。 誤って消費される前にnull値がコードベースに移動することに起因するバグを追跡するのが難しいため、値を消費する前にタイプガードを使用するように強制する型定義を記述したいと思います。

interface Legacy { 
  get<T>(...):Promise<T|void>
}

function isVoid(val: any): val is void { return val == null; } // also captures undefined

legacy.get(...).then(val => {
  // val.consumeNormally() is a type error here
  if (!isVoid(val)) { 
    val.consumeNormally(); // OK
  }
  else { handle null case }
});

これは私には完全に合理的に見えます。 追加void労働組合には、上のすべての操作原因val 、彼らがあるべきとして、無効であると。 タイプガードは|voidを削除してタイプを絞り込み、 T部分を残します。

おそらく、仕様は無効な組合の影響を考慮に入れていませんか?

void変数を禁止しないでください! これらはユニットタイプの値として機能し、使用できます
式内(2セットのすべてを必要とするC#のvoidとは異なり:1
アクションと1つの機能用)。 ボイドを放っておいてくださいね
2015年1月27日午後8時54分、「GorgiKosev」 [email protected]は次のように書いています。

私が上で書いたコードはafaik完全に有効なTS1.5ですよね?

確かにナンセンスではない例を紹介します。 私たちは
残念ながら、LinqのSingle()に似たデータベースライブラリ関数
Promiseを返します代わりに、エラー(拒否された約束)をスローする
アイテムが見つかりません。 コードベース全体で何千もの使用されています
場所があり、交換されたり、すぐになくなる可能性はありません。 書きたい
私と他の開発者にタイプガードの使用を強制するタイプ定義
数十の追跡が難しいバグがあったため、値を消費する前に
null値がコードベースに移動する前にコードベースに移動することに起因します
誤って消費されました。

インターフェイスレガシー{
得る(...):約束
}
function isVoid(val:any):val is void {return val == null; } //未定義もキャプチャします

legend.get(...)。then(val => {
// val.consumeNormally()はエラーです
if(!isVoid(val)){
val.consumeNormally(); // わかった
}
else {nullの場合を処理する}
});

これは私には完全に合理的に見えます。 どの部分が
ナンセンス?

このメールに直接返信するか、GitHubで表示してください
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -71767358

私が言っているのは、 T|voidは、タイプとそのサブタイプの和集合が単にスーパータイプと同等であるという規則がすでにあるため、現在は本質的に無意味であるということです。たとえば、 Cat|Animalはと同等です。 Animalvoidを値null / undefined代用として扱うことは、 nullundefinedがすでに_にあるため、一貫性がありません。 T _のドメイン(すべてのT )。これは、 Tサブタイプである必要があることを意味します。

言い換えれば、 T|null|undefined 、それを書くことができれば、すでにTに崩壊する可能性があります。 voidnull|undefined呪文として扱うのは間違っています。なぜなら、それが実際に当てはまる場合、コンパイラはすでにT|voidT折りたたんでいるからです。

私は個人的にこれを読みますhttp://www.typescriptlang.org/Content/TypeScript%20Language%20Specification.pdf

Voidタイプに使用できる値は、nullおよび未定義のみです。 Voidタイプは、Anyタイプのサブタイプであり、NullタイプとUndefinedタイプのスーパータイプですが、それ以外の場合、Voidは他のすべてのタイプとは無関係です。

として; T|voidでは、(仕様によると)唯一のスーパータイプはanyため、Tは具体的にはvoidスーパータイプではありません。

編集:それを禁止することは別の問題です。

Edit2:あなたが言ったことを読み直してください、そしてそれは文字通り(ドキュメンテーションが無効と言っている方法で)意味があります、しかしそれはドキュメンテーションの一般的な(または正しい)解釈であると思われるものでは意味がありません。

@RyanCavanaughとにかく機能が壊れるまで使用します。それまでの間、多くのバグを見つけるのに役立つ可能性があります。

そして、 T|null|undefinedT折りたたむことは「理にかなっている」が、 T|void存在はそうではないと言う皮肉を指摘せずに

@jbondc

ECMAScriptには静的構造型がないため、引数は無効です。 ECMAScriptでは、すべての値の型はanyため、任意のものを任意のものに割り当てたり、任意のものに渡すことができます。 その事実は、JSに存在しないものを改善するためにあるTypeScriptの静的型システムについて何も意味するものではありません。

皮肉を説明しなければならないのは少し悲しいですが、ここに行きます:

  1. メソッドもプロパティもサポートしない値null
  2. この値を任意の型の変数に割り当て可能な型システムを作成します。それらのほとんどは、さまざまなメソッドとプロパティ(たとえば、文字列番号や複雑なオブジェクトvar s:string = null )をサポートします。
  3. その値を正しく受け入れ、メソッドまたはプロパティを許可しないvoidタイプを作成します。その結果、 T|voidはメソッドとプロパティを許可しません。

主張は、(3)はナンセンスであるが、(2)はナンセンスではないというものです。 ここで皮肉な部分が見えますか? そうでない場合は、別の例を試してみます。

  1. 一部のメソッドとプロパティのみをサポートする値があります{}
  2. この値が、はるかに多くのメソッドとプロパティのセットを持つ任意の型の変数(たとえば、文字列番号または複雑なオブジェクトvar s:string = {} )に割り当て可能な型システムを作成します。
  3. {}値を正しく受け入れ、いくつかの組み込みのもの以外のメソッドまたはプロパティを許可しない{}タイプがあり、 T|{}当然、 {}組み込みのもの

さて、どちらがナンセンスです、(2)または(3)?

2つの例の唯一の違いは何ですか? いくつかの組み込みメソッド。

最後に、インターフェースは単なる型定義の説明でした。 どこにでも実装されているインターフェースではありません。 ライブラリによって提供されるインターフェースであり、promise以外のものを返すことはありませんが、nullのpromiseを返す場合があります。 したがって、安全性は間違いなく非常に現実的です。

@spionそれはまさに私が考えていたものです。

ここで、これで少し問題が解決すると思います。
仕様によると:


ボイドとは何ですか?

voidキーワードによって参照されるVoidタイプは、値がないことを表し、戻り値のない関数の戻りタイプとして使用されます。

したがって、voidはnull | undefinedではありません。

Voidタイプで可能な値はnullで未定義のみです

したがって、nullおよびundefinedはvoidに割り当てることができます(他のほとんどすべてに割り当てることができるため)。


ヌルはタイプです。

3.2.5ヌルタイプ
Null型は、同様の名前のJavaScriptプリミティブ型に対応し、nullの型です。
リテラル。
nullリテラルは、Nullタイプの唯一の値を参照します。 Nullタイプ自体を直接参照することはできません。


Undefinedはタイプです。

3.2.6未定義のタイプ
Undefined型は、同様の名前のJavaScriptプリミティブ型に対応し、undefinedリテラルの型です。


Voidは、NullおよびUndefined_types_のスーパータイプにすぎません。

。 Voidタイプは、Anyタイプのサブタイプであり、NullタイプとUndefinedタイプのスーパータイプですが、それ以外の場合、Voidは他のすべてのタイプとは無関係です。


したがって、 Typescript言語仕様によると、 anyを除く)に暗黙的にキャストされません。 実際、Voidは、NullタイプやUndefinedタイプとは異なる動作をするように特別に作成されています。

空所:

Voidタイプは、Anyタイプのサブタイプであり、NullタイプとUndefinedタイプのスーパータイプですが、それ以外の場合、Voidは他のすべてのタイプとは無関係です。

ヌル:

Nullタイプは、Undefinedタイプを除くすべてのタイプのサブタイプです。 これは、nullがすべてのプリミティブ型、オブジェクト型、共用体型、および数値型とブール型のプリミティブ型を含む型パラメーターに対して有効な値と見なされることを意味します。

未定義:

未定義のタイプは、すべてのタイプのサブタイプです。 これは、undefinedがすべてのプリミティブ型、オブジェクト型、共用体型、および型パラメーターに対して有効な値と見なされることを意味します。

今より明確ですか?

私たちが求めているのは、nullのガードを強制するタイプと、未定義のガードを強制するタイプです。 私はnullやundefinedを他のタイプに割り当てられないように求めているのではなく(それはあまりにも壊れています)、追加のマークアップにオプトインする機能だけを求めています。 ちなみに、それらはNullまたはUndefined(タイプ)と呼ばれる必要はありません。

@jbondcこれは皮肉なことです。TSコンパイラが何を{}を文字列に割り当てることを許可するのとほぼ同じくらいナンセンスです。 TSのvoid型には値が含まれていません。_but_ nullまたはundefinedはすべての人に割り当て可能であるため、void型の変数に割り当てることができるため、そこにはもう少しナンセンスがあります。 そしてそれは皮肉なことです-それは(タイプガードとユニオンを介して)両方とも現在実際に意味のあるものに結合しています:)

TSコンパイラのように複雑なものがガードなしで作成されたので、それについて考える必要があります。

PHPまたはJAVAで記述されたかなり大きなアプリケーションがありますが、言語を良くしたり悪くしたりすることはありません。
エジプトのピラミッドは現代の機械なしで作られていますが、それは私たちが過去4500年に発明したすべてががらくたであることを意味しますか?

@jbondc nullおよび未定義の値(リストの最初の値を除く)によって引き起こされるTypeScriptコンパイラの問題上記の私のコメントを参照し

@jbondc世界の問題を解決することは想定されておらず、開発者が自由に利用できる別のツールであることが想定されています。
これは、関数のオーバーロード、ユニオン、および型エイリアスと同じ影響と有用性を持ちます。
これは、開発者が値をより正確に入力できるようにするもう1つのオプトイン構文です。

型をnull不可にするために言及された大きな問題の1つは、既存のTypeScriptコードをどうするかです。 ここには2つの問題があります:1)null許容型をサポートしない新しいコンパイラで既存のコードを機能させる2)更新されていないコードと相互運用する-Python2 / 3スタイルの泥沼を回避する

ただし、既存のTSコードの自動書き換えを実行してビルドするツールを提供することは可能ですが、この限られたケースでは、Pythonの2to3よりもはるかにうまく機能します。

おそらく、段階的な移行は次のようになります。

  1. nullの可能性のある型を示すために、「?T」構文のサポートを導入します。 最初は、これは型チェックには影響しません。ドキュメント用にあります。
  2. '?T'を使用するように既存のコードを自動書き換えするツールを提供します。 最も愚かな実装は、「T」のすべての使用を「?T」に変換します。 よりスマートな実装では、型を使用したコードが暗黙的にnull以外であると想定しているかどうかを確認し、その場合は「T」を使用します。 タイプ '?T'の値を 'T'を期待するパラメーターまたはプロパティに割り当てようとするコードの場合、これは、値がnullではないことを(コンパイル時に)アサートするプレースホルダーで値をラップする可能性があります-例: let foo: T = expect(possiblyNullFoo)またはlet foo:T = possiblyNullFoo!
  3. 提供されているすべての型定義とDefinitelyTypedリポジトリ内の型定義にツールを適用します
  4. 'T'を期待するスロットに '?T'を割り当てようとすると、コンパイラの警告が発生します。
  5. 警告を実際に焼き付けて、移植が管理可能であることを確認し、変更がどのように受け取られるかを確認します
  6. コンパイラのメジャーな新しいリリースで、「T」エラーを予期しているスロットに「?T」を割り当てます。

おそらく、ツールがまだ適用されていないコードの使用を容易にするために、特定のモジュールの警告を無効にする何かを導入する価値があるかもしれません。

前のコメントでは、言語の分割を避けるためにプレーンな「T」のnull可能性を選択するモードはなく、ステップ6が実用的でないと証明されたとしても、ステップ1〜3はそれ自体で役立つと想定しています。

私のランダムな考え:

  1. expect()部分は複雑かもしれません、それはよく考えられる必要があるでしょう。 割り当てだけでなく、インターフェイス、ジェネリック、またはパラメータタイプもあります...
  2. TSには警告はなく、エラーのみがあります。 私が正しければ、彼らがその機能のためだけに警告を導入するかどうかはわかりません。
  3. 多くの人がコードをアップグレードしたとしても、企業はアップグレードに非常に時間がかかる場合があります。また、既存の大規模なコードベースの場合は、アップグレードしたくない場合もあります。 これにより、そのメジャーリリースは大きな重大な変更になります。これはTSチームが望んでいないことです。

そうは言っても、nullエラーを報告するための「オプトイン」フラグは許容できる解決策だと私はまだ考えています。 問題は、フラグがオフのとき、同じコード/定義が同じ動作(エラーを除く)を持つはずであるということです。これは、私がまだ考える時間がなかったものです。

@ jods4 -のためにexpect()一部、私の考えは、あなたが持っているところはどこでもあることです?Tコンパイラは見T | undefinedあなたがこれまでのように労働組合の種類を処理するためのルールを再利用できるよう可能。

TypeScriptにとって「国旗制定記念日」がどれほど現実的かわからないことに同意します。 「オプトイン」フラグとは別に、最終的にオプトアウトフラグになるオプトインフラグである可能性もあります。 重要なことは、慣用的なスタイルが何であるかについて明確な方向性を持つことです。

この議論に関連する他の何か-https ://github.com/rwaldron/tc39-notes/blob/master/es6/2015-01/JSExperimentalDirections.pdfは、タイプ情報(TypeScriptスタイルで指定)を使用するためのChromeチームによる調査について説明しています注釈)JSを最適化するためのコンパイラ。 そこにはnull可能性についての未解決の質問があります-彼らはnull許容型ではないことを望んでいますが、実現可能性についても確信がありません。

ここに戻ってチャイムを鳴らします。 私たちはこれを本当に解決していないことに驚いています。

ここに付加価値があると言います。 コンパイル時にのみ適用できるもの。
値をnullチェックするためにさらにコードを記述する代わりに、値をnull許容ではないと宣言できると、時間とコーディングを節約できます。 型を「null不可」と表現する場合、null以外を期待する別の関数に渡される前に、初期化され、場合によってはnull以外としてアサートされる必要があります。

上で述べたように、これはコードコントラクトの実装によって簡単に解決できるかもしれません。

nullリテラルの使用をやめて、すべての場所を保護できないのはなぜですか
nullがコードに漏れる可能性がありますか? このようにして、nullから安全になります
どこでもnullのチェックを行う必要なしに例外を参照します。
2015年2月20日午後1時41分、「electricessence」 [email protected]は次のように書いています。

ここに戻ってチャイムを鳴らします。 私たちが本当に解決していないことに驚いています
これです。

ここに付加価値があると言います。 コンパイル時にのみ適用可能なもの
時間。
値をnullチェックするためにさらにコードを記述する代わりに、
値をnull許容ではないと宣言すると、時間とコーディングを節約できます。 私が説明する場合
'non-nullable'と入力すると、初期化され、場合によってはアサートされる必要があります
期待する別の関数に渡される前にnull以外として
null以外。

上で述べたように、これはコードコントラクトによって簡単に解決できるかもしれません
実装。

このメールに直接返信するか、GitHubで表示してください
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -75295204

ヌルチェックは、未定義のチェックよりも大幅に高速です。
2015年2月21日午前11時54分、「AlekseyBykov」 [email protected]は次のように書いています。

nullリテラルの使用をやめて、すべての場所を保護できないのはなぜですか
nullがコードに漏れる可能性がありますか? このようにして、nullから安全になります
どこでもnullのチェックを行う必要なしに例外を参照します。
2015年2月20日午後1時41分、「electricessence」 [email protected]
書きました:

ここに戻ってチャイムを鳴らします。 私たちが本当に解決していないことに驚いています
これです。

ここに付加価値があると言います。 コンパイル時にのみ適用可能なもの
時間。
値をnullチェックするためにさらにコードを記述する代わりに、
値をnull許容ではないと宣言すると、時間とコーディングを節約できます。 私が説明する場合
'non-nullable'と入力すると、初期化する必要があり、場合によっては
主張
期待する別の関数に渡される前にnull以外として
null以外。

上で述べたように、これはコードコントラクトによって簡単に解決できるかもしれません
実装。

このメールに直接返信するか、GitHubで表示してください
<<
https://github.com/Microsoft/TypeScript/issues/185#issuecomment-75295204>


このメールに直接返信するか、GitHubで表示してください
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -75346198

これらのチェックは不要であるという私のポイント(nullキーワードが禁止されている場合)
そしてそれが入ることができる他のすべての場所は守られています)。

どうして? 1.チェックなしのコードは、コードよりも大幅に高速に実行されます
nullをチェックします。 2. ifが少なく、読みやすい
とメンテナンス可能。
2015年2月20日19:57、「Griffork」 [email protected]は次のように書いています。

ヌルチェックは、未定義のチェックよりも大幅に高速です。
2015年2月21日午前11時54分、「AlekseyBykov」 [email protected]は次のように書いています。

nullリテラルの使用をやめて、すべての場所を保護できないのはなぜですか
nullがコードに漏れる可能性がありますか? このようにして、nullから安全になります
どこでもnullのチェックを行う必要なしに例外を参照します。
2015年2月20日午後1時41分、「electricessence」 [email protected]
書きました:

ここに戻ってチャイムを鳴らします。 私たちが本当に解決していないことに驚いています
これです。

ここに付加価値があると言います。 コンパイル時にのみ適用可能なもの
時間。
値をnullチェックするためにさらにコードを記述する代わりに、

値をnull許容ではないと宣言すると、時間とコーディングを節約できます。 もし私が
説明する
'non-nullable'と入力すると、初期化する必要があり、場合によっては
主張
期待する別の関数に渡される前にnull以外として
null以外。

上で述べたように、これはコードコントラクトによって簡単に解決できるかもしれません
実装。

このメールに直接返信するか、GitHubで表示してください
<<
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -75295204

このメールに直接返信するか、GitHubで表示してください
<<
https://github.com/Microsoft/TypeScript/issues/185#issuecomment-75346198>

このメールに直接返信するか、GitHubで表示してください
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -75346390

あなたは要点を見逃しています。未定義のチェックを避けるためにnullを使用します。
そして、私たちのアプリケーションには1つの一貫した状態がないため、
多くの場合、オブジェクトの代わりにnull / undefinedにする必要があります
それを表します。
2015年2月21日12:20 PMに、「AlekseyBykov」 [email protected]は次のように書いています。

これらのチェックは不要であるという私のポイント(nullキーワードが禁止されている場合)
そしてそれが入ることができる他のすべての場所は守られています)。

どうして? 1.チェックなしのコードは、コードよりも大幅に高速に実行されます
nullをチェックします。 2. ifが少なく、読みやすい
とメンテナンス可能。
2015年2月20日19:57、「Griffork」 [email protected]は次のように書いています。

ヌルチェックは、未定義のチェックよりも大幅に高速です。
2015年2月21日午前11時54分、「AlekseyBykov」 [email protected]
書きました:

nullリテラルの使用をやめて、すべての場所を保護できないのはなぜですか
どこ
nullがコードに漏れる可能性がありますか? このようにして、nullから安全になります
どこでもnullのチェックを行う必要なしに例外を参照します。
2015年2月20日午後1時41分、「electricessence」 [email protected]
書きました:

ここに戻ってチャイムを鳴らします。 私たちが本当にしていないことに驚いています
解決しました
これです。

ここに付加価値があると言います。 でのみ適用可能なもの
コンパイル
時間。
値をnullチェックするためにさらにコードを書く必要はなく、
できる

値をnull許容ではないと宣言すると、時間とコーディングを節約できます。 もし私が
説明する
'non-nullable'と入力すると、初期化する必要があり、場合によっては
主張
期待する別の関数に渡される前にnull以外として
null以外。

私が上で言ったように、多分これはコードによって簡単に解決できるでしょう
契約
実装。

このメールに直接返信するか、GitHubで表示してください
<<

https://github.com/Microsoft/TypeScript/issues/185#issuecomment -75295204

このメールに直接返信するか、GitHubで表示してください
<<
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -75346198

このメールに直接返信するか、GitHubで表示してください
<<
https://github.com/Microsoft/TypeScript/issues/185#issuecomment-75346390>


このメールに直接返信するか、GitHubで表示してください
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -75347832

あなたも私のポイントを逃しています。 状態の一貫性を維持し、欠落している値を表現できるようにするために、nullや未定義を使用する必要はありません。 他の方法がありますが、特に現在、共用体タイプのサポートでは次のことを考慮してください。

class Nothing { public 'i am nothing': Nothing; }
class Something { 
    constructor(
    public name: string,
    public value: number
   ) { }
}
var nothing = new Nothing();
var whoami = Math.random() > 0.5 ? new Something('meh', 66) : nothing;

if (whoami instanceof Nothing) {
    console.log('i am a null killer');
} else if (whoami instanceof Something) {
    console.log(whoami.name + ': ' + whoami.value);
}

したがって、nullまたはundefinedを使用せずに、欠落している値をエンコードしただけです。 また、 100%明示的に行っていることにも注意してください。 タイプガードのおかげで、値を読み取る前にチェックを見逃すことはありません。

それはどれくらいクールですか?

チェックのインスタンスはヌルチェックよりも速いと思いますか?
毎秒何百ものこれらを実行しなければならないアプリケーション?
つまり、関数のエイリアスを作成する必要がありました。
'。'を使用しない方が高速です。 アクセサー。
2015年2月21日12:58 PMに、「AlekseyBykov」 [email protected]は次のように書いています。

あなたも私のポイントを逃しています。 あなたの状態を一貫して保つためにそして
nullを使用する必要のない欠落値を表すことができるか
未定義。 他の方法がありますが、特に現在、共用体タイプがサポートされています
検討:

class Nothing {public'i am none ':Nothing; }
クラス何か{
コンストラクタ(
パブリック名:文字列、
公的価値:数
){}
}
var none = new Nothing();
var whoami = Math.random()> 0.5? new Something( 'meh'、66):なし;

if(whoami instanceof Nothing){
// whoamiは何もない
console.log( '私はヌルキラーです');
} else if(whoami instanceof Something){
// whoamiはSomethingのインスタンスです
console.log(whoami.name + ':' + whoami.value);
}

したがって、nullまたはundefinedを使用せずに、欠落している値がエンコードされただけです。
また、_100%明示的な方法_で行っていることにも注意してください。 タイプに感謝
警備員、値を読み取る前にチェックを見逃すことはできません。

それはどれくらいクールですか?


このメールに直接返信するか、GitHubで表示してください
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -75349967

@ aleksey-bykovすでにその点に取り組んでいることに注意してください。すでにnull値を使用している既存のライブラリの型定義をモデル化する必要性は、解決されていません。 その場合、null値の「使用をやめる」ことはできません。

また、それはundefinedなる初期化されていない変数の問題にも触れていません。 Haskellでは、値を「初期化されていない」ままにすることはできないため、問題は存在しません。

@Griffork 、私はそうは思いません、私はテストを実行します、テストはそれがブラウザに依存すると言います。
http://jsperf.com/nullable-vs-null-vs-undefined-vs-instanceof
安全性と性能のバランスをとる必要があると思います。 次に、パフォーマンスを最適化する前に、パフォーマンスを注意深く測定する必要があります。 壊れていないものを修正している可能性があり、心配しているnullチェックが全体のパフォーマンスの2%未満になる可能性があります。その場合、2倍速くすると、1%しか得られません。

@spion 、現在の状況とnull不可能なものがもたらす問題を考えると、コードでnullを使用し続けるよりもはるかに優れた方法であると考えられるすべてのもの

私たちのコードベースは800以上のTypeScriptファイルと120000行以上のコードです
モデリングビジネスに関しては、nullや未定義は必要ありませんでした
ドメインエンティティ。 また、DOM操作にはnullを使用する必要がありましたが、
そのような場所はすべて慎重に隔離されているため、ヌルがリークすることはありません
で。nullが必要になるかもしれないというあなたの議論を買わない、
nullがまったくない(Haskell)またはnullがある本番環境に対応した言語
禁止(F#)。
2015年2月22日午前9時31分、「Jon」 [email protected]は次のように書いています。

@ aleksey-bykovhttps ://github.com/aleksey-bykov実用的なものから
パースペクティブでは、nullを無視することはできません。 ここにあなたがする理由のいくつかの背景があります
とにかくnull許容型が必要です:
https://www.google.com/patents/US7627594?ei=P4XoVPCaEIzjsATm4YGoBQ&ved=0CFsQ6AEwCTge

このメールに直接返信するか、GitHubで表示してください
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -75437927

はい、そしてここで理解できる最も重要なことは、私たちはビジネスドメインエンティティをモデル化しておらず、時間に非常に敏感であり、コードベースはあなたのものよりもそれほど小さくないということです。

あなたの目的のためにあなたはヌルを必要としません、それは問題ありません、あなたはそれらなしでTypescriptを使い続けることができます。
私たちの目的にはヌルが必要であり、そうしないわけにはいきません。

ああ、あなたの他のコメントを見ただけです、ごめんなさい。
このスレッドの少し前に読んだ場合、私が(本質的に)常にnullを使用しており、他のコードへの浸透に問題が発生することはめったにないことに気付くでしょう。

上記も読んだ場合、null許容でない提案は未定義もカバーします。

ヌルと変数の未定義の速度は似ていますが、オブジェクト(特にプロトタイプチェーンを持つオブジェクト)に存在するとすぐにストーリーが大きく異なるため、代わりにヌルを使用します。

繰り返しになりますが、(私たちのように)10ミリ秒未満で10,000回のチェックを行っていない限り、おそらく心配する価値はありません。

(はい、アプリケーションのパフォーマンスを追跡し、ボトルネックを見つけたため、実際に変更しました)

これについての議論に拍車をかけたいと思います。 昨日のビルドビデオで、アナライザーがC#で実装されているのを見ました。 TypeScriptと同様に実装するために問題#3003を開きました。

基本的に、アナライザーは、追加の構文チェックを実行し、行をエラー/警告としてタグ付けし、言語サービスによって解釈できるライブラリーです。 これにより、外部ライブラリでnullチェックを効果的に実施できます。

実際、TypeScriptのアナライザーは、他のことにも非常に役立ちます。 C#よりも、本当に奇妙なライブラリがあります。

この問題をフォローしている人のために:私は、変数のnull可能性を宣言できる

フィードバックを歓迎します!

スレッド全体を読んだだけです。

TS仕様のvoidnullundefined値のタイプであるため、提案ではnon-voidablevoidable用語を使用する必要があると思います。 。 (私は提案が未定義の値もカバーしていると思います:smile :)

また、アクセスが未定義であるかnull例外がプログラミングのすべての悪の根源であることに同意します。この機能を追加すると、人々の数え切れないほどの時間を節約できます。

これを追加してください! そして、これが機能になり、フラグを渡してデフォルトにすることができれば最高です。 それは多くの人々のために多くのデバッグ地獄を救うでしょう。

null許容型の代わりにADTを使用できることに同意できますが、 渡すADT自体がnullではないことをどのように確認できますか? ADTを含め、TSのどのタイプもnullになる可能性がありますよね? これは、ADTをnull許容型(void不可)型の問題の解決策として考える上での大きな欠陥だと思います。 ADTは、null許容型ではないタイプを許可(または禁止)する言語でのみ機能します。

もう1つの重要な問題は、ライブラリの定義です。 引数として文字列を期待するライブラリがあるかもしれませんが、TSコンパイラは関数にnullを渡してチェックを入力できます! それは正しくありません...

問題の解決策としてADTを考える上での大きな欠陥だと思います...

  1. デザインパターンを通じてADTを利用できるようにする(ネイティブが不足しているため)
    TypeScriptからのサポート)
  2. nullキーワードを禁止する
  3. nullが外部からコードに漏れる可能性のあるすべての場所を保護します
  4. null問題を永久に楽しんでください

私たちのプロジェクトでは、ステップ1、2、3を実行できました。何をしているかを推測します。
今。

@ aleksey-bykov

undefined (2)どうしますか? これは、 undefinedキーワードを含まないさまざまな状況で発生する可能性があります。初期化されていないクラスメンバー、オプションのフィールド、条件分岐でのreturnステートメントの欠落...

typescriptflowtypeと比較し

また、「言語の一般的なイディオムを忘れてください。気にしないで、他の世界から身を守る」と言っても構わないと思っているなら、 purescriptを使用したほうがよいでしょう。

未定義のキーワードを含まないさまざまな状況で発生する可能性があります...

  • 初期化されていないクラスメンバー-禁止されているクラス
  • オプションのフィールド-オプションのフィールド/パラメータは禁止されています
  • 欠落しているreturnステートメント-すべての実行パスが確実に返されるようにするカスタムtslintルール

purescriptを使用する方が良い

  • 私は望みますが、それが生成するコードはパフォーマンスに最も関係しません

@ aleksey-bykovパフォーマンスについて言及するのはおかしいです。なぜなら、null許容型をADTに置き換えると、nullチェックを強制するだけの場合(flowtypeなど)に比べて、CPUとメモリのオーバーヘッドが大幅に増えるからです。 クラスを使用しない場合も同様です(多数のメソッドを含む多数のオブジェクトをインスタンス化すると、非常にコストがかかる可能性があります)

オプションのフィールドをあきらめるとおっしゃっていますが、それらは非常に多くのJSライブラリで使用されています。 それらの図書館に対処するためにあなたは何をしましたか?

また、クラスを使用しないことについても言及しています。 クラス(Reactなど)に依存する(または依存する)ライブラリも避けていると思いますか?

いずれにせよ、完全に合理的なソリューション(実際には基礎となる型なし言語に適合する)を実装できる可能性が最も高い場合、それほど多くの機能をあきらめる理由はわかりません。

ADTの唯一の目的は、nullでエンコードされていたはずの欠落した値を表すことだと思うのはおかしいです

むしろ、「null」の問題がなくなった(そしてクラスも)ことは、ADTが私たちにもたらすすべての良いことの副産物であると言いたいです

好奇心に対処するために、タイトなループや大きなデータ構造などのパフォーマンスが重要な部分については、TypeScriptをだましてラップされた値を処理していると思わせることで、いわゆるNullable<a>抽象化を使用しますが、実際には(実行時)そのような値はnullを取得できるプリミティブの場合、Nullableには、そのnullがリークするのを防ぐ特別な一連の操作があります。

詳細を知りたい場合はお知らせください

interface Nullable<a> {
    'a nullable': Nullable<a>;
    'uses a': a;
}
/*  the following `toNullable` function is just for illustration, we don't use it in our code,
    because there are no values capable of holding naked null roaming around,
    instead we just alter the definition of all unsafe external interfaces:
    // before
    interface Array<T> {
       find(callback: (value: T, index: number, array: T[]) => boolean, thisArg?: any): T;
    }
    // after
    interface Array<a> {
       find(callback: (value: a, index: number, array: a[]) => boolean, thisArg: any): Nullable<a>;
    }
*/
function toNullable<a>(value: a) : Nullable<a> {
    return <any>value;
}
function toValueOrDefault<a>(value: Nullable<a>, defaultValue: a)  : a {
    return value != null ? <any>value : defaultValue;
}

なぜ再帰型構造を使用するのですか? 'a nullable': aで十分ですよね?

nullを返すすべての外部関数を処理するためのcallFunctionWithNullableResult<T,U>(f: (T) => U, arg:T):Nullable<U> (異なるアリティを持つ関数の他のすべてのオーバーロードを含む)もあると思いますか?

また、nullキーワードがリンターによって禁止されている場合、nullableに「 Nothing 」を格納するにはどうすればよいですか? 特別な場合はtoNullable(null)ですか?

私は代数的データ型が何であるかを知っています。 ADT(というよりは合計型)+パターンマッチングがtypescriptの共用体+ユーザー定義の型ガードができないことを実行できるインスタンス(網羅性チェックと不格好さ以外)を教えていただけますか?

なぜ再帰型構造を使用するのですか? 'nullable':aで十分なはずですよね?

まあ、この例ではそれで十分ですが、このようにすると、そのようなフィールドの目的が過負荷になり、2つの異なるケースに役立ちます。

  • タイプを名義に変換するには(例ではファイル名): https
  • ジェネリック型パラメーターを認識して、オブジェクトの表面(例では値の型)まで考慮されるようにする: https

私は個人的にオーバーロードが嫌いです。これが、それぞれが別々の問題を解決する2つの別々の疑似フィールドを使用する理由です。

callFunctionWithNullableResultもあると思います(f:T-> U、arg:T)=> Nullable

いいえ、私が言ったように、外部コードでnullになる可能性のある証拠をNullable 「ラッパー」に置き換えることで定義(* .d.ts)ファイルを変更します。コメントの例を参照してください。私の前のメッセージで

null許容型に「Nothing」を格納するにはどうすればよいですか

nullableをnullableに格納しません。nullableはサーバー(制御が制限されています)または外部APIから取得されます。コードに含まれるとすぐに、Nullableにラップされますが、コードは生成できません。 nullableは自由に使用できます。特定のnullableを別のnullableに変換することはできますが、薄い空気からnullableを作成することはできません。

typescriptのユニオン+ユーザー定義のタイプガードはできません

ハ! 私は共用体タイプのファンではなく、何時間も彼らを悪い考えにするものについて話すことができます

質問に戻ります。ユニオンとタイプガードを使用して、再利用可能なEither a b相当するものを教えてください
(ヒント:https://github.com/Microsoft/TypeScript/issues/2264)

あなたの例の型は、再帰的であるという理由だけでより名目上のものでです

ただし、質問に答えるには、型が名目上ではないという理由だけで、ジェネリックのどちらかを実装しません。 あなたは単に使用します

type MyType = A | B

次に、 ABタイプガードを使用します...ボーナスとして、これは既存のすべてのJSライブラリでも機能します。たとえば、 Thenableをチェックする慣用的な方法はオブジェクトにアタッチされたthenメソッドです。 タイプガードはそのために機能しますか? 確かに。

ハ! それはあなたのひどい良いキャッチです。 さて、コンパイラの最近のバージョンで何かが変更されたに違いありません。 防弾名目は次のとおりです。

enum GottaBeNonimalBrand {}
interface GottaBeNonimal {
     branded: GottaBeNonimalBrand;
}
function toGottaBeNominal() : GottaBeNominal {
   return <any> {};
}

一瞬待って! 騙された! 古き良き方法は今でも魅力のように機能します。

ただし、質問に答えるには、型が名目上ではないという理由だけで、ジェネリックのどちらかを実装しません。 あなたは単に使用します...

それはあなたが言うことは素晴らしいです、しかしそれはどうですかEither ? その本物のEitherGENERICであり、その周りには約30の標準的な便利な関数があります。出会い?

うまくいけば、このスレッドはまだTSチームの人々によって監視されています。 再起動してみたい!
ノーブレークの方法でこれを導入することが不可能であることが明らかになったとき、非ヌル型がスタックしたように私には思えます。 C#チームは、null許容でない参照型について考えており、もちろん同じ問題、特に互換性に直面しています。 彼らが現在模索しているアイデアが好きで、TSに適用できるのではないかと思います。

C#vnextブレインストーム(簡単に)

基本的に、C#には、 intなどのnull以外の値型があります。 次に、C#2.0では、 int?などのnull許容値型を取得しました。 参照は常にnull許容ですが、これを変更して同じ構文を適用することを検討しています。 stringはnullになることはなく、 string?はnullになる可能性があります。

もちろん、大きな問題は、これによってstringの意味が変わり、コードが壊れることです。 nullstringに割り当て/返却しても問題がなかった場合は、エラーになります。 C#チームは、オプトインしない限り、これらのエラーを「無効にする」ことを考えています。 string?点在することは常にエラーです。これは、構文が新しく、以前は許可されていなかったためです(したがって、重大な変更ではありません)。
もう1つの問題は、他のライブラリなどにあります。これに対処するために、ファイルまたはアセンブリごとにオプトインフラグを作成する必要があります。 したがって、より厳密な型システムを選択する外部コードには利点があり、古いレガシーコードとライブラリは引き続き機能します。

それはどのようにTSに置き換えることができますか?

  1. null以外のタイプのオプトインフラグを導入します。 グローバル(コンパイラーに渡される)またはファイルごとの場合があります。 .d.ts場合、常にファイルごとである必要があります。
  2. 基本型はnull許容ではありません(例: number 。 新しいnullタイプがあります。
  3. number?は、単にnumber | nullショートカットです。
  4. この機能により、型ガードなしの(x: string?) => x.lengthなど、null許容型の新しいエラーが発生します。
  5. この機能により、 let x: string = null;割り当てなど、null許容型ではない新しいエラーが発生します。
  6. オプトインスコープ外で宣言されたnull以外の型を操作する場合、5で報告されたエラーは無視されます。 _これは、 stringがまだstring?と見なされると言うのとは少し異なります。_

これは何がいいですか?

新しいコードを書くとき、オプトインして、コードと更新されたライブラリ定義で完全な静的nullチェックを取得できます。 エラーなしでレガシーライブラリ定義を引き続き使用できます。

古いコードを使用すると、新しいファイルをオプトインできます。 とにかくstring?を宣言すると、適切なnullチェックなしでメンバーにアクセスするとエラーが発生します。 また、定義が更新されたライブラリの利点と厳密なチェックも受けられます。 .d.tsがオプトインすると、 length(x: string): numberとして定義された関数にnullを渡すとエラーになります。
_(注:オプトアウトコードからstringを渡すことはエラーではありませんが、 string?を渡すこともエラーです。)_

オプトインしない限り、重大な変更はありません。

どう思いますか?

もちろん、この機能が実際にどのように機能するかについては、考慮すべきことがたくさんあります。 しかし、そのようなオプトインスキームは、TSチームが検討する可能性があるものですか?

人々が自動車の代わりにもっと速い馬を望んでいるのは驚くべきことです。

見下す必要はありません。 ADTについてのコメントを読みましたが、私が必要としているものではないと思います。 必要に応じてそれについて話し合うかもしれませんが、「TSでのADTサポート」に関する新しい問題で。 なぜそれらが優れているのか、なぜそれらが必要なのか、そしてそれらがnull許容型ではないタイプの必要性をどのように軽減するのかをそこに書くことができます。 この問題はnull許容型ではなく、実際にトピックを乗っ取っているという問題です。

あなたが話していることは定数伝播と呼ばれ、実装されている場合、たまたまnullundefined 2つのランダム定数によってのみ制限されるべきではありません。 文字列、数字、その他何でも同じことができます。そうしないと、一部の人々がまったく望んでいないいくつかの古い習慣を養うためだけになります。

@ aleksey-bykovそしてもちろん、より良い型制限を取得するには、より高い種類の型を実装し、派生インスタンスを取得するには、型クラスなどを実装します。 これは、TypeScriptの「ES6 +との整合」設計目標にどのように適合しますか?

そして、この問題から元の問題を解決するのにまだすべて役に立たない。 今日でも(共用体型なしで)ジェネリッククラスを使用してTSに多分(およびどちらか)を実装するのは簡単です。 これは、JavaにOptionalを追加するのと同じくらい便利です。つまり、かろうじてです。 誰かがnullどこかに割り当てるか、オプションのプロパティが必要になるため、言語自体は文句を言いませんが、実行時に爆発します。

なぜあなたが言語を乱用して、それが決して設計されていなかった方法でそれを曲げさせることを主張するのか私にはわかりません。 代数的データ型を持ち、null値を持たない関数型言語が必要な場合は、PureScriptを使用してください。 その優れた、うまく設計された言語は、一貫して一緒にフィットし、Haskellの蓄積された疣贅(原理的な型クラス階層、行多相を伴う実際のレコード...)なしでHaskellで長年にわたって戦闘テストされています。 私には、あなたの原則とニーズに準拠した言語から始めて、パフォーマンスを微調整する方がはるかに簡単であるように思われます。あなたのやり方で働きなさい。

PureScriptのパフォーマンスの問題は何ですか? それは、広汎性カリー化によって生成されたクロージャですか?

@ jods4は、現在存在するすべての.d.tsファイルを作業停止フラグで更新する必要があること、またはプロジェクト全体の設定が.d.ts(および.d.ts)に影響を与えないことを意味します。フラグなしは常に「古い」方法であると見なされます。
これはどの程度理解できますか(複数のプロジェクトで作業している人に問題を引き起こしたり、.d.tsファイルを読み取るときに混乱を引き起こしたりしますか)?
新しいコードと古い(ライブラリ)コード間のインターフェイスはどのようになりますか。 たくさんの(場合によっては)不必要なチェックとキャストが散らかっていますか(おそらく悪いことではありませんが、初心者が言語をオフにする可能性があります)?
ほとんどの場合、私は(現在)null許容型ではないという考えが好きで、 @ jods4による提案と!提案は、null許容型ではない(adtsを無視する)動作を確認する唯一の方法です。

このスレッドで失われた可能性がありますが、これはどのような問題を解決しますか? それは私をほのめかしたものです。 私がこのスレッドで最近見た議論は「他の言語がそれを持っている」です。 私はまた、TypeScriptでなんらかの策略を必要としない、これが何を解決するのかを理解するのに苦労しています。 nullundefined値は望ましくない可能性があることは理解できますが、これは、基になる言語が処理することを期待するのではなく、コードに入れたい高階述語論理と見なす傾向があります。 。

もう1つの小さな問題は、誰もがundefinedを定数またはキーワードとして扱っていることです。 技術的にはそうではありません。 グローバルコンテキストでは、プリミティブ型undefined変数/プロパティundefinedあります。 nullはリテラルです(これもプリミティブ型nullですが、 typeof "object"を返すように実装されているのは長年のバグです)。 ECMAScriptでは、これら2つのことはまったく異なります。

@kitsonk
問題は、すべての型がnullになる可能性があり、すべての型が未定義になる可能性があるが、特定の型で機能するすべての操作がnullまたは未定義の値(たとえば除算)で機能しないためにエラーが発生するわけではないことです。
彼らが望んでいるのは、型を「数値であり、nullまたは未定義になることはない」として指定できることです。これにより、nullまたは未定義のプログラマーを導入する可能性のある関数を使用するときに、無効な値を防ぐことができます。
値を「このタイプですが、nullまたは未定義にすることはできません」として指定できるようにするための2つの主な引数は次のとおりです。
A)コードを使用するすべてのプログラマーにとって、この値がnullまたは未定義であってはならないことは明らかです。
B)コンパイラは、欠落している型ガードをキャッチするのに役立ちます。

プリミティブ型が未定義の変数/プロパティが未定義です。 nullはリテラルです(これもプリミティブ型nullです

@kitsonkこれはこの問題の素晴らしい議論です。 nullないではありませんnumberそれはより多くのですが、 PersonよりCarです。 これは独自のタイプであり、この問題は、typescriptがこの区別を行えるようにすることに関するものです。

@kitsonk最も一般的なエラーの1つは、null参照/アクセス未定義エラーだと思います。 プロパティまたは変数をnull /未定義にならないように指定する機能があると、ソフトウェアコードがより健全になります。 また、LSなどのツールでこのようなバグをキャッチできるようにしますhttps://github.com/Microsoft/TypeScript/issues/3692。

説明してくれてありがとう。 たぶん、これを振り返って、ここでも何かが欠けているのかもしれません... TypeScriptにはすでに「オプション」の引数があるのに、なぜ以下が提案として機能しないのでしょうか。

interface Mixed {
    optional?: string;
    notOptional: string;
    nonNullable!: string;
}

function mixed(notOptional: string, notNullable!: string, optional?: string): void {}

提案されたソリューションのいくつかにはより優れた機能がありますが、それらに対するタイプの保護が課題になると思います。これは(TypeScriptの内部動作はよくわかりませんが)、すでにチェックされているものの拡張のようです。

TypeScriptにはすでに「オプション」の引数があるのに、なぜ以下が提案として機能しないのでしょうか。

現在TSのオプションを明確にするために、初期化中のオプションのみです。 Nullおよびundefinedは、引き続きすべてのタイプに割り当てることができます。

これらは、オプションのいくつかの直感的でないケースです

interface A {
   a?: string;
}
let a: A = {} // ok as expected

interface B {
   b: string;
}
let b1: B = { b: undefined } //ok, but unintuitive
let b2: B = { b: null } // ok, but unintuitive
let b3: B = {} // error as expected

または、TSでoptionalを単純に保つことは、初期化できないことを意味します。 しかし、この提案では、undefinedとnullを非voidable(non-null、non-undefined)型に割り当てることはできないとしています。

?は、プロパティ/引数の名前の_existence_を参照しているかどうかに関係なく、その名前で配置されます。 @tinganhoはそれをうまく説明しました。 彼の例を拡張する:

function foo(arg: string, another?: string) {
  return arguments.length;
}

var a: A = {};
a.hasOwnProperty('a') // false
foo('yo') // 1, because the second argument is _non-existent_.
foo(null) // this is also ok, but unintuitive

null以外の!は、名前の_existence_とは無関係です。 タイプに関連しているため、タイプごとに配置する必要があります。 タイプはnull許容ではありません。

interface C {
  c: !string;
}

let c1: C = { }; // error, as expected
let c2: C = { c: null }; // error, finally!
let c3: C = { c: 'str' }; // ok! :)

function bar(arg: !string) {
  return arg.length;
}

bar(null) // type error
bar('foo') // 3

@Griffork既存の.d.tsは正常に機能し、より厳密な定義をサポートするためにスムーズに更新できます。
デフォルトでは、 .d.tsはオプトインしません。 ライブラリの作成者がnull以外の忠実な定義でライブラリを更新する場合は、ファイルの先頭にフラグを設定することでそのことを示します。
どちらの場合も、消費者の考えや構成がなくても、すべてが機能します。

@kitsonkこれが解決する問題は、バグを減らすことです。なぜなら、人々は何かがnullでない可能性があると想定したり、それをサポートしていない関数にnullを渡したりするからです。 多くの人が参加する大規模なプロジェクトで作業する場合、自分が作成していない関数を使用する可能性があります。 または、よく知らないサードパーティのライブラリを使用する場合もあります。 let x = array.sum(x => x.value)を実行する場合、 xがnullになることはないと想定できますか? 配列が空の場合は0かもしれませんか? どうして知っていますか? あなたのコレクションはx.value === nullを持つことができますか? それはsum()関数でサポートされていますか、それともエラーになりますか?
この機能を使用すると、これらのケースが静的にチェックされ、nullの可能性のある値をサポートしていない関数に渡すか、チェックなしでnullの可能性のある値を消費するとエラーとして報告されます。
基本的に、完全に型指定されたプログラムでは、「NullReferenceException」は発生しなくなります(残念ながら、 undefinedに関連するコーナーケースを除く)。

オプションのパラメータに関する説明は関係ありません。 nullを許可するかどうかは、変数/パラメーター宣言ではなく、型システムに関連しており、型ガード、和集合、交差型などとうまく統合されます。

タイプ_void_には値がありませんが、 nullundefined両方の値を割り当てることができます

ここに良い要約があります: https

@jbondc最近スペックをチェックしたと思います。 voidは、 nullundefined総称です。 void 0についてはわかりませんが、 voidも傘下にあると思います。

参照: https

any推測します。 ただし、 nullundefinedは、引き続きすべてのタイプに割り当てることができます。

void 0常にundefinedも返しますhttp://stackoverflow.com/a/7452352/449132。

@jbondc質問が、ポテンシエルnull認識型システムに関するものであった場合、

function returnsNull() {
  return null;
}

タイプnullを推測する必要があります。

undefined周りには多くのコーナーケースがあり、正直なところ、私はそれに取り組むための最良の方法が何であるかについて(まだ?)確信がありません。 しかし、それについて考える前に、TSチームがオプトイン戦略で大丈夫かどうかを知りたいと思います。

前回このディスカッションに参加したときは、「重大な変更は行わず、アンビエントフラグ/オプションに基づいてソースコードの意味を変更したくない」で終了しました。 上で説明したアイデアは、重大な変更ではなく、ソースの解釈に関しては100%ではありませんが、比較的優れています。 その灰色の領域が、TSチームの考えを尋ねる理由です。

@ jods4私は、既存の構文を維持する、推測されたnull許容のanyを好みます。 そして、オプトイン戦略が望ましいでしょう。 [1]

null許容型ではない型には明示的な構文を使用したいと思います。 オブジェクトの場合、一度にいくつかの多くのものを壊してしまいます。 また、オプションの引数は、明らかな理由で常にnull許容である必要があります(変更することは不可能である必要があります)。 デフォルトの引数は、現在のシグニチャを外部で保持する必要がありますが、関数自体の内部では、引数をnull不可にすることができます。

ただし、null許容の数値は、エンジンが実行できる最適化の多くを破り、オプション/デフォルトの引数以外の一般的なユースケースではないため、これはばかげていると思います。 オプションの引数ではない数値を、コンパイラフラグを介してデフォルトでヌル不可にすることは、もう少し実行可能/非破壊的である可能性があります。 これには、null許容型を明示的にマークするための構文も必要になりますが、この議論の結果として、すでにそれを見ています。

[1]これをデフォルトにするフラグを導入することは、実際的な理由から、そもそもうまく機能しません。 人々が--noImplicitAnyで遭遇した問題に目を通すだけで、いくつかの移行の危険、 --suppressImplicitAnyIndexErrorsにつながるプロパティの存在のテストにおける多くの実際的な問題、およびいくつかの壊れたDefinitelyTyped定義が発生しました。

@impinball

既存の構文を維持する、推測されたnull許容のany勧めします。 そして、オプトイン戦略が望ましいでしょう。

ここで答えを編集しました。 これについてもっと考えてみると、 nullタイプのみを暗黙的に推測できる便利なケースは見当たりません。 例: () => nullまたはlet x = nullまたはfunction y() { return null }

だから私はanyを推測し続けることはおそらく良いことだと同意します。

  1. 下位互換性があります。
  2. これらのケースはすべて、とにかくnoImplicitAny未満のエラーです。
  3. それを実行したいという奇妙なケースがあり、それが役立つ場合は、タイプを明示的に宣言できます: (): null => nullまたはlet x: null = nullまたはfunction y(): null { return null }

null許容型ではない型には明示的な構文を使用したいと思います。 オブジェクトの場合、一度にいくつかの多くのものを壊してしまいます。

string実際にはstring! | null意味すると言うことは、別のオプションかもしれません。 上記のすべての議論を読んだ場合、これは最初に提案されたものであることに注意してください。これは、既存のコードとの互換性が高まる(現在のstring意味を変更しない)という希望があったためです。 しかし実際には、それでも、とにかく大量のコードが壊れてしまうという結論に達しました。

null許容型ではない型システムを採用することは、既存のコードベースにとって簡単な切り替えではないことを考えると、 string!string?オプションを選択します。 特に、TSコンパイラのソースコードを見ると、ほとんどすべての内部型の名前が不正確になっています:(

オプションの引数ではない数値を、コンパイラフラグを介してデフォルトでヌル不可にすることは、もう少し実行可能/非破壊的である可能性があります。 これには、null許容型を明示的にマークするための構文も必要になりますが、この議論の結果として、すでにそれを見ています。

この時点で物事は混乱していると思います。 私にとって、これはどこでもstring?を使用するための良い議論のように思えます。 number?string!混同するのを見たくありません。これは、すぐに混乱しすぎてしまいます。

[1]これをデフォルトにするフラグを導入することは、実際的な理由から、そもそもうまく機能しません。

はい、変更を加えないと、既存のコードベースではうまく機能しません。 しかし:

  1. これは、新しいコードベースに最適です。 これでより良い未来を築くことができます。
  2. 既存のコードベースは、オプトインしなかったとしても、_いくつかの_利点と追加のチェックを享受します。
  3. 既存のコードベースはファイルごとにオプトインできます。これにより、新しいコードをより厳密にすることができ、必要に応じて古いコードを段階的に移行できます。

@ jods4

この時点で物事は混乱していると思います。 私にとって、これは文字列を使用するための良い議論のように思えますか? どこにでも。 数を混同したくないですか? と文字列!、これはあまりにも早く混乱するでしょう。

そういう意味ではありません。 つまり、null許容文字列よりもnull許容数値のユースケースが少ないということです。 とにかく、私はその提案にそれほど執着していません(どちらの方法でも気にしません)。

既存のコードベースはファイルごとにオプトインできます。これにより、新しいコードをより厳密にすることができ、必要に応じて古いコードを段階的に移行できます。

どういう意味ですか? 現在、ファイルごとにコンパイラを構成できるとは思いません。 私はここで間違っていることが証明されることを開いています。

@impinball

どういう意味ですか? 現在、ファイルごとにコンパイラを構成できるとは思いません。 私はここで間違っていることが証明されることを開いています。

多分私は間違っています。 私はそれらを使用したことがありませんが、テストケースを見ると、 // <strong i="9">@module</strong> amdようなコメントがたくさんあります。 私はいつもそれがtsファイルの中からオプションを指定する方法だと思っていました。 しかし、私はそれをやろうとしたことがないので、おそらく私は完全に間違っています! ここで例を見てください:
https://github.com/Microsoft/TypeScript/blob/master/tests/cases/conformance/externalModules/amdImportAsPrimaryExpression.ts

これがファイルごとのオプションを指定する方法でない場合は、何か新しいものが必要になる可能性があります。 このオプションはファイルごとに指定する必要があります。 .d.tsファイルの場合は少なくとも_。 ファイルの先頭にある特別な形式のコメントで十分な場合があります。結局のところ、 /// <amd-dependency />とcoはすでにサポートされています。

編集:私は間違っています。 ソースコードを調べてみると、これらはマーカーと呼ばれ、FourSlashランナーによって事前に解析されていることがわかりました。 したがって、これはテスト専用であり、コンパイラ内ではありません。 そのためには、何か新しいことを考え出す必要があります。

場合によっては実際的な制限もあります。ファイルを完全に再解析する必要があるため、ES6機能などの一部の引数では最も実用的ではありません。 そして、IIUCの型チェックは、そもそもAST作成と同じパスで行われます。

パーサーコードを見ると、 /// <amd-dependency />/// <amd-module />コメントは、ファイル内で他の何かが解析される前に読み取られます。 /// <non-null-types />ようなものを追加することは可能です。 OK、それはひどい命名であり、私は恣意的な「魔法の」オプションの急増のためではありませんが、それは単なるアイデアです。

@ jods4私は@I「Mのピンボールは、IDEには、構文は作品をハイライト表示する方法に大きな変更を加えることなく、それをサポートしていないことを言っていると思います。

2.0にはnull許容型ではない形式はありますか?
結局のところ、typescriptは、型を保証できない場合、javascriptにもたらすものは非常に少なくなります。typescriptによって導入されたすべての問題と書き直しを克服した後に何を得ることができますか?
ちょうど良いIntelliSense? 他のモジュールにエクスポートするすべての関数で、手動またはコードでタイプを確認する必要があるためです。

皆さん、朗報です。TypeScript1.6には、欠落している値( nullundefined値でエンコードされている)をモデル化して安全に追跡するのに十分な表現力があります。 基本的に、次のパターンはnull許容型ではありません。

declare module Nothing { export const enum Brand {} }
interface Nothing { 'a brand': Nothing.Brand }
export type Nullable<a> = a | Nothing;
var nothing : Nothing = null;
export function isNothing(value: Nullable<a>): value is Nothing {
    return value == null;
}
var something = Math.random() > 0.5 ? 'hey!' : nothing;
if (isNothing(something)) {
    // missing value
    // there is no way you can get anything out of it
    // there is also NO WAY to get a null reference exception out of it
    // because it doesn't have any methods or properties that could be examined
    // it is 100% explicit and typesafe to use
} else {
    // value is present, it is 100% GUARANTEED being NON-NULL
    // you just CANT get a null reference exception here either
    console.log(something.toLowerCase());
}

/** turns any unsafe values into safe ones */
export function sanitize<a>(unsafe: a) : Nullable<a> {
    return unsafe;
}

var safe = sanitize(toResultFromExternalCodeYouCannotTrust()); // <-- 100% safe to use

そうは言っても、もう問題はないので、リクエストを閉じる必要があります。 ケースはクローズされ、クラスは却下されました。

@ aleksey-bykov

そうは言っても、もう問題はないので、リクエストを閉じる必要があります。

真面目なことはできませんね。 null許容代数型のパターンはこの問題ではなかったと何度も言いました。

コード内のすべてのvar x: numberNullable<number>またはさらに良いNonNullable<x>にラップするつもりvar x: number 。 それはあまりにも多くのオーバーヘッドです。 それでも、 x *= 2ことは、コード内でこれが発生する場所であればどこでも100%安全であることを知りたいです。

あなたのコードはサードパーティライブラリを使用する問題を解決しません。 私は、私が呼び出す_every_サードパーティライブラリ、または組み込みのDOMAPIでsanitizeを呼び出すつもりはありません。 さらに、 isNothing(safe) _すべての場所に_追加したくありません。 私が望んでいるのは、 let squares = [1,2,3].map(x => x*x)を呼び出して、コンパイル時にsquares.lengthが安全であることを100%確信できることです。 私が使用する_any_APIを使用します。

同様に、関数が入力としてnullを受け入れるかどうかについて、サードパーティのJSライブラリのドキュメントが必要です。 コンパイル時に$(element).css(null)がOKかエラーか知りたいのですが。

全員があなたのような複雑なパターンを一貫して使用することを保証できないチーム環境で働きたいです。 Nulllableタイプは、開発者がlet x: number = null; x.toString()を実行するのを妨げるものではありません(または、それほど愚かではありませんが、同じ効果があります)。

などなど。 このチケットはクローズから_遠い_ですが、問題はまだ100%あります。

真面目なことはできませんね。

私はかなり真剣です。

私はすべてを包むつもりはありません...

なぜだめですか? !構文など、皆さんがプッシュしているものを使用すると、とにかくそれを実行する必要があります。

またはさらに良いNonNullable

モデリングしているのはNullableである必要があります。これは、 不可の値またはnullを持つ可能性のあるnull許容型であり明示的に記述されています。 常に値またはnullを持ち、 numberと呼ばれる従来のタイプとは異なり、使用する前にnullをチェックする必要があることを意味します。

それはあまりにも多くのオーバーヘッドです。

どこ?

あなたのコードはサードパーティライブラリを使用する問題を解決しません。

します。

私が呼び出すすべてのサードパーティライブラリ、または組み込みのDOMAPIでさえsanitizeを呼び出すつもりはありません。

あなたはする必要はありません。 あなたがする必要があるのは、nullになる可能性のあるすべてのものをNullable<*>置き換えることによって、そのサードパーティライブラリの定義ファイルを修正することです

同様に、関数が入力としてnullを受け入れるかどうかを示すサードパーティのJSライブラリのドキュメントが必要です。

同じこと。 そのメソッドを、プレーンなstringではなくNullable<string>を受け入れるように定義します。

Nulllableタイプは、開発者がlet x:number = null;を実行するのを妨げることはまったくありません。 x.toString()

確かにそうではありません。 リンターを使用してnullキーワードを禁止する必要があります。

私のソリューションは95%が機能し、100%実用的であり、難しい決定を下したり、全員を同じページに配置したりすることなく、今日利用できます。 それはあなたが何を求めているのかという問題です:あなたが期待するものには見えないがそれでも機能する実用的なソリューション、またはすべてのバックジャックとチェリーを上にしてあなたが望むように正確にそれを得る

1.4および1.5では、void型は.toString()などのObjectのメンバーを許可しないため、モジュールを必要とせずにtype Nothing = void;で十分です(1.6で再度変更された場合を除く)。 http://bit.ly/1OC5h8d

@ aleksey-bykovそれは本当に真実ではありません。 nullの可能性があるすべての値をサニタイズするように注意する必要があります。 それを忘れても警告は表示されません。 確かにその改善です(注意すべき場所が少なくなります)。

また、ハッキングが実際にあなたを遠くまで連れて行かないことを説明するために、試してみてください

if (isNothing(something)) {
  console.log(something.toString())
}

私はすべてを包むつもりはありません...

なぜだめですか? と ! 構文や他の皆さんがプッシュしているものは何でも、とにかくそれをしなければならないでしょう。

OK、他のすべてが解決されれば、私はそれを行うことができます。 そして、すべてが解決されれば、TSはそのための言語の砂糖を考慮することさえできるかもしれません。

またはさらに良いNonNullable

モデリングしているのはNullableである必要があります。これは、null不可の値またはnullを持つ可能性のあるnull許容型であり、明示的に記述されています。 常に値またはnullを持ち、numberと呼ばれる可能性がある従来のタイプとは異なり、使用する前にnullをチェックする必要があります。

null例外を取り除きたいだけではありません。 null許容型も静的に安全に表現できるようにしたいと思います。
常に値を返すメソッドがあるとします。 toString()ように、常にnull以外の文字列を返します。
そこにあなたの選択肢は何ですか?
let x: string = a.toString();
x.lengthが安全であるという静的な検証がないため、これは適切ではありません。 この場合はそうですが、あなたが言ったように、組み込みのstringnull可能性が高いので、それが_現状_です。
let x = sanitize(a.toString());
OK、nullチェックなしでは使用できないので、コードは安全です。 しかし、コードでxを使用するすべての場所にif (isNothing(x))を追加したくありません。 xがコンパイル時にnullでないことを私はよく知っているので、これは醜く非効率的です。 (<string>x).lengthを実行する方が効率的ですが、 xを使用したい場所でそれを実行しなければならないのは、それでも地獄のように醜いです。

私がやりたいことは:

let x = a.toString(); // documented, non-null type string (string! if you want to)
x.length; // statically OK

JS(およびTS 1.6)のすべてのタイプは常にヌル可能であるため、適切な言語サポートなしではこれを達成することはできません。

オプションではない(null許容)型を使用したプログラミングは、可能な限り非常に良い方法であると言いたいです。 したがって、ここで説明しているのは、例外ではなく、本質的なシナリオです。

それはあまりにも多くのオーバーヘッドです。

どこ?

私の以前の答えを参照してください。

あなたのコードはサードパーティライブラリを使用する問題を解決しません。

します。

問題の半分だけが解決されます。 あなたが提案したようにライブラリ定義が更新されたと仮定すると、すべてのnull許容入力または戻り値はXではなくNullable<X>に置き換えられます。

nullパラメーターを受け入れない関数にnullを渡すことはできます。 OK、この事実は現在_文書化されています_(JSDocコメントなどですでに実行できます)が、コンパイル時に_強制_する必要があります。
例: declare find<T>(list: T[], predicate: (T) => bool) 。 両方のパラメーターがnullであってはなりません(私はNullable使用していません)が、 find(null, null)実行できます。 もう1つの考えられるエラーは、述語からnull以外のboolを返す必要があると宣言されているにもかかわらず、 find([], () => null)実行できることです。

Nulllableタイプは、開発者がlet x:number = null;を実行するのを妨げることはまったくありません。 x.toString()

確かにそうではありません。 linterを使用してnullキーワードを禁止する必要があります。

そうして、代わりにnothingグローバル変数を提供しても、前のポイントでリストしたケースでは役に立ちませんか?

私の見解では、これは95%ではありません。 多くの一般的な事柄で構文が大幅に悪化したように感じますが、null許容型ではない型について話すときに必要な静的な安全性がまだすべてありません。

nullの可能性があるすべての値をサニタイズするように注意する必要があります。

とにかく、ここで話している架空のnull許容型ではないタイプで実行する必要があるのとまったく同じ種類の分離作業です。 すべての定義ファイルを確認し、必要に応じて!する必要があります。 それはどう違うのですか?

それを忘れても警告は表示されません。

同じこと。 同じこと。

ハックが実際にあなたを遠くまで連れて行かない方法を説明してください、ただ試してみてください

私が定義した方法は正しいのですが、 Object toStringから@ ArnavionvoidNothingに使用する)のアイデアを採用した場合Nothing )それはすべて突然クリックします

時計

image

私の見解では、これは95%ではありません。 多くの一般的な事柄で構文が大幅に悪化したように感じますが、null許容型ではない型について話すときに必要な静的な安全性がまだすべてありません。

男、あなたは私を納得させました、このパターンはあなたのためではなく、決してそうなることはありません、あなたのコードでそれを決して使用しないでください、本当のnull許容型ではないことを求め続けてください、幸運、そのようなナンセンスであなたを悩ませて申し訳ありません

@ aleksey-bykov
私はあなたが何をしようとしているのかわかりますが、あなたはまだすべてのケースを解決していないようで、誰かが穴を指摘したときに解決策を作っているようです( null代用など)最後の例ではvoid )。

結局、あなたはそれを私が望むように機能させるでしょう。 null許容型には複雑な構文を使用し、プログラム内のnullソースをリンターで禁止し、最終的にnull許容型をnull以外にする可能性があります。 その過程で、非標準の規則を使用してライブラリを更新するように全員を説得する必要があります。そうしないと、まったく役に立ちません(これは、あなただけでなく、null問題のすべての解決策にも当てはまります)。

最後に、組み込みのnull型を回避し、 Nothing型にチェックを添付するために、型システムをひねることに成功する可能性があります。

この時点で、言語はそれをサポートするだけでよいと思いませんか? この言語は、(内部的に)希望する方法とほぼ同じ方法ですべてを実装できます。 しかし、それに加えて、優れた構文、アイロンがけされたエッジケース、外部のリンターの必要がなく、サードパーティの定義によるより良い採用が確実に得られます。 それはもっと意味がありませんか?

この時点で、言語はそれをサポートするだけでよいと思いませんか?

私の考えについて話しましょう。 明日、TypeScriptにnull許容型がない世界で目を覚ますのはいいことだと思います。 実際のところ、これは私が毎晩寝るという考えです。 しかし、それは翌日には決して起こらず、私はイライラします。 それから私は仕事に行き、私の人生を楽にすることができるパターンを探すことにした最近まで、ヌルで同じ問題を何度も繰り返しました。 見つけたようです。 TypeScriptにnull不可能なものがあればいいのにと思いますか? 確かに私はします。 私は彼らと欲求不満なしで生きることができますか? できるようです。

TypeScriptにnull不可能なものがあればいいのにと思いますか? 確かに私はします。

では、なぜこの問題を解決したいのですか? :スマイリー:

そうは言っても、もう問題はないので、リクエストを閉じる必要があります。 ケースはクローズされ、クラスは却下されました。

あなたがあなたのニーズに対する解決策を見つけてくれてうれしいですが、あなたのように、いつの日かTSが適切なサポートを受けることを願っています。 そして、私はこれがまだ起こるかもしれないと信じています(すぐには気にしないでください)。 C#チームは現在同じブレインストームを行っており、おそらくこれは物事を前進させるのに役立つかもしれません。 C#7がnull以外の型を取得できた場合(まだわかりません)、TSが同じことを行わない理由はありません。

@ aleksey-bykov

簡単にするために

var nothing: void = null;
function isNothing<a>(value: a | void): value is void {
    return value == null;
}
var something = Math.random() > 0.5 ? 'hey!' : nothing;

これで、関数ラッパーなしでも使用できるようになりました。適切な型定義で十分です。

それは基本的にそれに依存することを思いとどまらせるまで、元々私をちょっと幸せにしていた回避策です

すべての定義ファイルを確認して配置する必要があります。 適切な場において。 それはどう違うのですか?

ハックの代わりに標準の公用語機能を使用する場合の主な違いは、コミュニティ(DefinitelyTyped)が、プロジェクトの上に積み重なる余分な作業の代わりに、定義ファイルの作成、テスト、共有を支援することです(実際には:笑顔:)。

私は「 --noImplicitNull 」を提唱しています。 これにより、すべてのタイプがデフォルトでnull不可になり、既存のコードの潜在的な問題に関するフィードバックがすぐに得られます。 「!」がある場合は、タイプチェッカーがnullの可能性を無視するようにSwiftのように動作する必要があります(null不可への安全でないキャスト)-これは、 PromiseのES7 +「最終的な送信」演算子と競合するため、最善ではない可能性があります考え。

それが下位互換性の破損という点で過激すぎると思われる場合、それは本当に私のせいです。 私は本当に時間を見つけてフォークで試して、見た目ほど過激ではないことを証明する必要があります。 実際に「!」を追加しますより急進的です。ほとんどの値は実際にはnullではないため、これを利用するにはさらに多くの変更が必要になります。 --noImplicitNullは、null以外の値を誤って要求する型定義を修正することでより多くのエラーを発見し、それらの修正がないと、null割り当て、初期化されていない値、およびオプションのオブジェクトフィールドのチェックを忘れたことを除いて、同じ動作を得るため、より段階的です。 。

ディスカッションに私の声を加えると、Kotlinが問題をどのように解決したかが好きです。

  • 独自の変数には、デフォルトではnull許容型はありません。「?」 それを変える
  • 外部変数はnull許容、「!!」 オーバーライド
  • nullチェックは型キャストです
  • 外部コードは、注釈を生成するツールで分析できます

比較:
1
2

  • a|voidが禁止されている場合は、 a|Nothing切り替えることができます

ハックの代わりに標準の公用語機能を使用する場合の主な違いは、コミュニティ(DefinitelyTyped)が定義ファイルの作成、テスト、および共有を支援するために存在することです。

  • 100%合法的な機能を使用することがハックである理由がわかりません
  • コミュニティのスキルを過大評価しないでください。そこにある多くの(ほとんどではないにしても)定義は低品質であり、たとえば、jqueryのようなものでさえ、いくつかの変更なしに記述されたとおりに使用することはできません。
  • 繰り返しになりますが、パターンは本格的な機能ほど良くはありません。誰もがそれを手に入れられることを願っています。
  • パターンは今日の問題を解決できますが、機能は近くにある場合とない場合があります
  • ABとCを使用することで状況を効率的に(最大95%)解決できる場合、なぜ誰もが同じことを行うためにDが必要になるのでしょうか。 砂糖を欲しがる? パターンでは解決できないことに焦点を当ててみませんか?

とにかく、問題が解決可能であることが示されたように、問題解決者と完璧主義者がいます

(void型に依存したくない場合は、同じ効果を持つclass Nothing { private toString: any; /* other Object.prototype members */ }になることもできません。#1108を参照してください)

_注:ここでは、構文をバイクシェッドすることについては何もありません。 服用しないでください
そのような。_

簡単に言えば、提案されたTS1.6ソリューションの問題は次のとおりです。
ここで:それは実行可能に機能することができますが、標準ライブラリまたは
ほとんどの定義ファイル。 また、数学演算子には適用されません。 お望みならば
冗長なタイプ/ランタイムボイラープレートについて文句を言うには、一般的なことをしてみてください
コレクションはC ++で動作します-これは、特に必要な場合は、比較すると見劣りします
配列ではない怠惰な反復またはコレクション(またはさらに悪いことに、試してみてください
2つを組み合わせる)。

@ aleksey-bykovによって現在提案されているソリューションの問題は
言語コアでは強制されません。 たとえば、あなたはできません
Object.defineProperty(null, "name", desc) -からTypeErrorを取得します
nullターゲットオブジェクト。 もう1つのこと:プロパティをに割り当てることはできません
nullのようにオブジェクト、 var o = null; o.foo = 'foo'; 。 あなたは
ReferenceErrorIIRC。 この提案されたソリューションでは、これらのケースを説明できません。
これが、言語サポートが必要な理由です。


_さて、少し穏やかなバイクシェディング..._

構文に関しては、「?string」の簡潔さと
「!string」構文ですが、最終結果に関しては、私がいる限り気にしません
ThisIsANullableType<T, U, SomeRidiculousAndUnnecessaryExtraGeneric<V, W>>書かない。

水曜日、2015年7月29日には、午前16時10分アレクセイBykovの[email protected]書きました:

  • a | voidが禁止されている場合は、a | Nothingに切り替えることができます。

    標準の公用語機能を使用する場合の主な違い
    ハックの代わりに、コミュニティ(DefinitelyTyped)が支援するためにそこにいるということです
    定義ファイルの作成、テスト、共有

    100%合法的な機能を使用することがハックである理由がわかりません

    コミュニティのスキルを過大評価しないでください。多くの人(ほとんどではないにしても)
    そこには低品質の定義があり、たとえばjqueryのようなものでさえかなり

    多くの場合、いくつかの変更なしに書かれたように使用することはできません

    繰り返しになりますが、パターンは本格的な機能ほど良くはありません。

    誰もがそれを手に入れる、間違いない

    パターンは今日の問題を解決することができますが、機能は可能性があります

    近くにいない

    ABを採用することで状況を効率的に(最大95%)解決できるかどうか
    とCなぜ誰かが同じことをするためにDが必要なのですか? 砂糖を欲しがる? なぜだろう
    パターンでは解決できないことに焦点を当てますか?

とにかく、示されているように、問題解決者と完璧主義者がいます
問題は解決可能です


このメールに直接返信するか、GitHubで表示してください
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -126082053

そして、それをペイントする色にこだわる前に、少なくとも自転車小屋の構造がどのように見えるかを知ることができますか? 最初の約10件のコメントの後で私がこれまでに見たもののほとんどは、「ねえ、私たちは自転車小屋を作りたい!それをどの色に塗るべきか?」です。 デザインが構造的に健全であることを確認することについては何もありません。 (これの他のいくつかの例については、#3192と#1206の前半を参照してください-論理的に作成された構文と完全に指定されたセマンティクスを使用して最終的に真剣な提案をしたときに、ほとんどのノイズはなくなりました。)

覚えておいてください:これは、標準ライブラリタイプ定義の主要なリファクタリングと部分的な書き直しをもたらす可能性が非常に高いです。 また、これをサポートするTSの最初のバージョン用に、DefinitelyTypedの型定義の大部分を書き直す必要があります。 したがって、これは間違いなく壊れることを覚えておいてください。 (解決策は、null関連のエラーが発生した場合でも、デフォルトで常に発行することですが、その動作を変更するために--noImplicitAnyフラグを提供することです。)

確かに、私はここで少し頭を抱えています。 それでも、私はscholar.googleで「nullablejavascript」と「nullabilityjavascript」を検索しました。
TypeScriptを理解する
TypeScript(モジュールジェネリック)の型の素晴らしいテーブルがあります。

JavaScriptの依存型
重要なようです。 ソースコードが消えた

信頼するが検証する:動的な2フェーズタイピング言語
スクリプト言語の改良タイプ
typescriptに基づくソリューションを提供します。
( "省略形t?はt + nullの略です。";#186のように聞こえます)

@afrischeこれの多くは、JSタイプチェッカーですでに実際に使用されています。 たとえば、フローは「信頼するが検証する」のイディオムのほとんどを使用します。 Infernuもあります。これは、Hindley-Milner型推論1に大きく依存して型を推論するWIPJS型チェッカーです。 しかし、私は逸脱します...

[1] Haskell、OCamlなどは、型システムにも修正バージョンを使用しています。

私は現在、型がデフォルトでnull許容ではないと想定し、(既存の)Null型をnullで参照できるようにするTSフォークをいじっています(例: string | nullstring?構文も追加しました。これは、同じ共用体型のショートカットにすぎません。

深く考えると、@ aleksey-bykovと同じですが、 nullNothingタイプの組み込みです。

そして、型システムはすでにこれらすべてを処理するのに非常に優れているので、それを行うのはそれほど複雑ではありません!

物事が厄介になるのは、スムーズな移行パスが必要なことです。 少なくとも既存の定義ファイルとの下位互換性が必要です(プロジェクト自体との互換性はオプトインフラグである可能性がありますが、コードの一部の意味(たとえばインターネット上で)がそうでなかった場合はより良いでしょう) tはグローバルコンパイラフラグに依存します)。

自分のプロジェクトでstring?を使用するが、 .d.ts string!を使用するという@afrischeのアイデアは、新しいアプローチになる可能性があります。 私はそれが生み出す恣意的な二重性はあまり好きではありませんが。 string null可能で、一部のファイルと他のファイルがnull不可であるのはなぜですか? 奇妙に思えます。

一部のファイルと他のファイルで文字列をnull許容にしたくない場合。
コンパイラにオプトアウトフラグを設定し、それを重大な変更として導入するというです(gasp:以前の引数からの大きな変更)。 もちろん、オプトアウトフラグを使用するときに、新しい構文でエラーが発生しないと仮定します。

このスレッドは1年以上前のものであり、関連するすべての設計と懸念事項を理解するのは困難です。300近くのコメントがあり、TSチーム自体からはほんのわずかです。

大規模なコードベースをFlow、Closure Processor、またはTypeScriptに移行することを計画していますが、TypeScriptにnullの安全性がないことは、大きな問題です。

スレッド全体を読まないと、 !?両方のnull可能性指定子を追加することの何が問題になっているのかを完全に理解することはできませんが、それらを欠く型の既存の動作を維持します。 :

提案

declare var foo:string // nullability unspecified
declare var foo:?string // nullable
declare var foo:!string // non-nullable

?string!stringスーパーセットであり、 string両方のスーパーセットとサブセットの両方である場合、つまりstringと両方の?string !stringは、 anyと他のすべてのタイプの関係と同じです。つまり、 !または?ないベアタイプはanyようなものです。無効性に関しては

次のルールが適用されます。

| タイプ| 含む| 提供| null割り当てることができますか? | nullはないと想定できますか? |
| --- | --- | --- | --- | --- |
| T | T!T?T | T?T!T | はい| はい|
| ?T | T!T?T | T?T | はい| いいえ(タイプガードが必要)|
| !T | T!T | T?T!T | いいえ| はい|

これにより、既存のコードを壊すことなくnullの安全性が提供され、 any既存のコードベースが型の安全性を段階的に導入できるように、既存のコードベースでnullの安全性を段階的に導入できます。

null参照エラーのあるコードを次に示します。

function test(foo:Foo) { foo.method(); }
test(null);

このコードはまだ合格です。 nullは引き続きFoo割り当てることができ、 Fooは引き続きnull以外であると見なすことができます。

function test(foo:!Foo) { foo.method(); }
test(null);

null!Foo割り当てられないため、 test(null)はエラーになります。

function test(foo:?Foo) { foo.method(); }
test(null);

?Fooメソッド呼び出しが許可されていないため、 foo.bar()はエラーになっています(つまり、コードはnullチェックする必要があります)。

ありがとう! そして、私はこのアイデアが本当に好きです。 ただし、互換性のために、
?TTArray<T>T[]ような単なるエイリアスにすることはできますか? これ
物事を簡素化するでしょう、そして装飾されていないバージョンはまだ技術的にです
null許容。

金、2015年8月28日には、午前7時14分ジェシーSchalkenの[email protected]書きました:

このスレッドは1年以上前のものであり、すべてが何であるかを理解するのは困難です。
関連する設計と懸念事項には、300近くのコメントがあり、ごくわずかです。
TSチーム自身から。

大規模なコードベースをFlow、ClosureCompilerまたは
TypeScriptですが、TypeScriptに型の安全性がないことは、大きな問題です。

スレッド全体を読まないと、何が悪いのかよくわかりません。
両方を追加して! と ? を維持しながらnullability指定子
それらを欠いている型の既存の振る舞いなので、ここでは提案として:
提案

宣言のvar fooの:文字列// NULL可能unspecifieddeclareするvar fooの?:文字列// NULL可能、宣言のvar FOO:!文字列// null非許容

?stringを使用すると、!stringのスーパーセット、およびスーパーセットとサブセットの両方の文字列
両方の、つまり文字列と?stringおよび!stringの両方の関係
他のすべてのタイプ間の関係と同じです。
ベアタイプなし! また ? nullibilityに関してはanyのようなものです。

次のルールが適用されます。
タイプContainsProvidesnullを割り当てることができますか? nullではないと仮定できますか? TT、!T、?TT、
?T、!Tはいはい?TT、!T、?TT、?Tはいいいえ(タイプガードが必要)!TT、!TT、
?T、!Tいいえはい

これにより、既存のコードを壊すことなくnullの安全性が提供され、
他のコードと同じように、nullの安全性を段階的に導入する既存のコードベース
既存のコードベースが型安全性を段階的に導入できるようにします。

null参照エラーのあるコードを次に示します。

function test(foo:Foo){foo.method(); } test(null);

このコードはまだ合格です。 nullは引き続きFooに割り当て可能であり、Fooは引き続き可能です。
null以外であると見なされます。

function test(foo:!Foo){foo.method(); } test(null);

nullは!Fooに割り当てられないため、test(null)はエラーになります。

function test(foo:?Foo){foo.method(); } test(null);

?Fooのメソッド呼び出しが許可されていないため、foo.bar()はエラーになります。
(つまり、コードはnullをチェックする必要があります)。


このメールに直接返信するか、GitHubで表示してください
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -135743011

@impinball

ありがとう! そして、私はこのアイデアが本当に好きです。 ただし、互換性のために、 ?TT Array<T>T[]ような単なるエイリアスにすることはできますか? それは物事を単純化するでしょう、そして装飾されていないバージョンはまだ技術的にnull可能です。

_全体のアイデア_は、 T?T 、および!Tは3つの異なるものであり、互換性(既存のTSコードとの互換性)のためです。 どう説明したらいいのかわからない、ごめんなさい。 つまり、私は小さなテーブルとすべてを作りました。

わかった。 いい視点ね。 その部分のテーブルを誤解して見落としていました
既存の定義ファイルが切り替わる場合、問題が発生します
他のアプリケーションとライブラリ。

金、2015年8月28日には、午前11時43ジェシーSchalkenの[email protected]書きました:

@impinball https://github.com/impinball

ありがとう! そして、私はこのアイデアが本当に好きです。 ただし、互換性のために、
配列のように、TとTを単なるエイリアスにすることができますか?とT []? それは
物事を単純化し、装飾されていないバージョンはまだ技術的にnull可能です。

_全体のアイデア_は、T、?T、!Tが3つの異なるものであるということです。
互換性のために_is_(既存のTSとの互換性)
コード)。 どう説明したらいいのかわからない、ごめんなさい。 つまり、私は
小さなテーブルとすべて。


このメールに直接返信するか、GitHubで表示してください
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -135810311

賢いお尻でごめんなさい、でもこれは意味がありません

?stringのスーパーセット!string 、およびstringの両方の両方のスーパーセットとサブセット

集合論から、集合Aはサブセットであり、集合BのスーパーセットはA = Bの場合にのみ存在することがわかります。

もしそうなら、あなたが「両方の」と言うとき、それは文字列のすべて=文字列のすべてと文字列のすべて=文字列のすべてを意味するだけです

したがって、最終的にはすべての文字列=すべての文字列

みんなが降りるところから、バスはどこにも行かず、バスは運行されていません

@ aleksey-bykovおそらくフレージングが不十分な場合。 彼は、 string?string | !stringに似ており、最も寛容な制約を採用していることを意味していると思います。

この全体は、Javaのますます一般的になっている@Nullableおよび@NonNull / @NotNullコンパイル時の型注釈、およびそれらが注釈なしの型でどのように機能するかと範囲が似ています。

そして、私は間違いなく、null不可能、特にプリミティブとして暗黙的に取られる型の新しいフラグに賛成です。

「デフォルトではnull不可」フラグは便利ですが、多数のDefinitelyTyped定義も壊してしまいます。 これにはAngularとNodeの定義が含まれており、修正するには多くの面倒な作業が必要になります。 また、バックポートが必要な場合もあるため、新しいタイプは構文エラーとして解析されませんが、null可能性はチェックされません。 このようなバックポートは、特にnull許容型の定義が更新されるため、このようなフラグを使用して破損を軽減する唯一の実用的な方法になります。 (人々はまだ開発、特に大規模なプロジェクトでTypeScript 1.4を使用しています。)

@impinball

人々はまだ開発、特に大規模なプロジェクトでTypeScript1.4を使用しています。

この機能の互換性の話は、新しい定義を古いコンパイラと互換性を持たせようとしない限り、すでに十分に難しいと思います(FWIWのnull以外の型は、現在のTSコンパイラに追加するのはほとんど簡単です)。

人々が古いTSにとどまる場合は、古い定義を使用する必要があります。 (私はそれが実用的ではないことを知っています)。
とにかく物事が壊れそうだということです。 間もなく、TS 1.6は交差型を追加し、それを使用する定義も互換性がなくなります。

ところで、私は人々が1.4にとどまっていることに驚いています、1.5にはたくさんの愛があります。

@ aleksey-bykov

賢いお尻でごめんなさい、でもこれは意味がありません

「スーパーセットと両方のサブセット」が何を意味するのかを投稿の残りの部分から完全によく知っているので、あなたは自分自身を「賢いお尻」と呼んでいると思います。 もちろん、実際のセットに適用しても意味がありません。

anyは、任意のタイプを割り当て(すべてのタイプのスーパーセットとして動作)、任意のタイプとして扱うことができます(すべてのタイプのサブセットとして動作します)。 anyは、「この値の型チェックをオプトアウトする」ことを意味します。

stringは、 !stringまたは?string両方から割り当てることができ(それらのスーパーセットとして動作します)、 !string?string両方として扱うことができます。 stringは、「この値のnull可能性チェックをオプトアウトする」こと、つまり現在のTSの動作を意味します。

@impinball

そして、私は間違いなく、null不可能、特にプリミティブとして暗黙的に取られる型の新しいフラグに賛成です。

@RyanCavanaughは明示的に言った:

言語のセマンティクスを変更するフラグは危険なものです。 [...]コードの一部を見ている人が型システムを「フォロー」し、行われている推論を理解できることが重要です。 言語のルールを変更するフラグの束を持ち始めると、これは不可能になります。

唯一安全な方法は、割り当て可能性のセマンティクスを同じに保ち、現在のnoImplicitAnyように、エラーとフラグに依存しないものを変更することです。

そのため、私の提案では、nullability指定子( !または? )がない型の既存の動作を維持しています。 追加する_only_安全フラグは、null可能性指定子がない型を許可しないフラグです。つまり、コードを渡すだけでエラーが発生し、その意味は変わりません。 同じ理由でnoImplicitAnyが許可されたと思います。

@jesseschalken

私は、暗黙的に型付けされた変数が初期化されていないという文脈で意味しました
次のような場合は、 nullまたはundefinedになります。

var a = new Type(); // type: !Type
var b = 2; // type: !number
var c = 'string'; // type: !string
// etc...

混乱させて申し訳ありません。

金、2015年8月28日には、午前19時54ジェシーSchalkenの[email protected]書きました:

@impinball https://github.com/impinball

そして、私は間違いなくタイプの新しいフラグに賛成です
暗黙的にnull不可、特にプリミティブと見なされます。

@RyanCavanaugh https://github.com/RyanCavanaughは明示的に言った:

言語のセマンティクスを変更するフラグは危険なものです。 [...]
コードの一部を見ている人が「フォロー」できることが重要です
型システムを使用して、行われている推論を理解します。 もしも
言語のルールを変更するフラグの束を持ち始めました、
これは不可能になります。

行うべき唯一の安全な種類のことは、のセマンティクスを維持することです
割り当て可能性は同じで、エラーと依存しないものを変更します
noImplicitAnyが今日どのように機能するかとよく似ています。

そのため、私の提案では、不足しているタイプの既存の動作を維持しています。
nullability指定子(!または?)。 追加する_only_安全フラグは
nullability指定子がないタイプを許可しないもの、つまりそれだけ
渡されたコードを受け取り、エラーを引き起こしますが、その意味は変わりません。 私
同じ理由でnoImplicitAnyが許可されたと仮定します。


このメールに直接返信するか、GitHubで表示してください
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -135916676

@jeffmcafferスーパーセットとサブセットという単語の元の意味を変更して言いたいことは、セットの観点から簡単に表現できます。 stringの値のセットは、 !string値の和集合です。 !stringおよび?string!stringまたは/および?stringstring属することを意味します

@impinball繰り返しますが、このようなフラグは既存のコードの意味を変更しますが、( @ RyanCavanaughのコメントを読むことから)許可されていません。 export var b = 5;は、以前はnumberエクスポートしていた場所に!numberエクスポートするようになりました。

はい、ある意味で。 もう少し技術的にするために、それは組合を受け入れますが、
それは交差点を提供します。 基本的に、どちらのタイプも
string 、およびstringは、どちらのタイプにも渡すことができます。

金、2015年8月28日には、20:20アレクセイBykovの[email protected]書きました:

@impinballhttps ://github.com/impinballあなたが言おうとしていること
スーパーセットとサブセットという単語の元の意味を変更しても、
セットの観点から簡単に明確に表現できます。文字列の値のセットは
!stringと?stringの値の_union_は、!stringからの何かを意味します
または/および?stringはstringに属します


このメールに直接返信するか、GitHubで表示してください
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -135920233

明らかに、デフォルトでオンになることを意図したものではありません。 そしてほとんどの定義ファイル
影響を受けません。 技術的には、純粋にではない変更はすべて
加法(関数に新しい引数を追加することさえJavaScriptにはありません)は
アプリケーションを壊す能力。 範囲は似ています
noImplicitAnyは、もう少し明示的な入力を強制するという点で。 そして私
特に唯一の理由から、それ以上に壊れる可能性があるとは思わないでください
これが他のファイルに影響を与える可能性がある方法は、実際のTypeScriptからのエクスポートによるものです。
ソースファイル。 (他のフラグは多数の定義ファイルを壊し、無効にされました
アヒルテストの頻繁な方法。)

金、2015年8月28日で、夜08時21ジェシーSchalkenの[email protected]書きました:

@impinball https://github.com/impinball繰り返しますが、そのようなフラグは変更されます
既存のコードの意味、これは(@RyanCavanaughを読んでから)
https://github.com/RyanCavanaughのコメント)は許可されていません。 エクスポート変数
b = 5; 以前は数値をエクスポートしていた場所に!numberをエクスポートします。


このメールに直接返信するか、GitHubで表示してください
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -135920315

明らかに、デフォルトでオンになることを意図したものではありません。

それはまだコードの意味を変えています。 これを行うとすぐに、TypeScriptコードを読んで「これが何を意味するのかわかります」と言うことも、他の誰かが使用するライブラリを作成することもできません。コードの意味は、使用されるフラグによって異なるためです。それをコンパイルしてください、そしてそれは受け入れられません。 あなたは文字通り言語を2つに分割しました。

そのため、私の提案では、nullability指定子( ?または! )がない型の既存の動作を維持しています。

@jesseschalken
既存のコードの意味を100%忠実に保持したい(そして管理したい)場合は、typescriptでのnullの安全性の考えをあきらめる必要があります。 実際に使用できるソリューションはありません。

すべての型注釈に?!配置する必要があることは、私がそれをしたくないのですでに十分に冗長ですが、すべての型推論をあきらめる必要もあります。 let x = 3は、今日number推測します。 ここで変更を受け入れない場合は、nullの安全性のメリットを享受するには、すべてを明示的に入力する必要があることを意味します。 私も喜んでやるようなことではありません。

利点が欠点を上回っている場合、いくつかの譲歩を行うことができます。 @impinballによって指摘されているnoImplicitAnyまさにそれを行いました。 これは、コードをオンにしたときにコードに新しいエラーを作成するフラグです。 そのため、インターネットからコードをコピーして貼り付けるか、TSライブラリを使用するだけでも、取り込んだコードがnoImplicitAny仮定の下で書かれていない場合、壊れることがあります(そしてそれは私に起こりました)。

ヌルセーフティも同様に導入できると思います。 コードの意味は同じであり、まったく同じセマンティクスで実行されます。 しかし、新しいフラグ(たとえば、 noImplicitNullなど)では、 noImplicitNull仮定で記述されていないコードから、追加のチェックと警告/エラーが発生します。

ヌルセーフティも同様に導入できると思います。 コードの意味は同じです
まったく同じセマンティクスで実行されます。 しかし、新しいフラグの下で(noImplicitNullなど)
で書かれていないコードから追加のチェックと警告/エラーが発生します
noImplicitNullの仮定。

私はそのアプローチが好きで、言語を進化させる論理的な方法のようです。 時間が経つにつれて、タイピングが通常noImplicitAnyを念頭に置いて記述されるのと同じように、デファクトスタンダードになることを願っています。

ただし、段階的な採用に関して重要なことは、既存のコードを一度にモジュールに移行できることと、明示的なnullを念頭に置いて記述された新しいコードが暗黙的なnullを使用する既存のコードで簡単に機能することを確認することです。

では、これはどうですか?

  • -noImplicitNullを使用すると、 T!Tエイリアスになります。 それ以外の場合、 Tのnull可能性は不明です。
  • フラグは、モジュールごとにオーバーライド可能である必要があります。たとえば、上部に注釈を追加します。 <strong i="18">@ts</strong>:implicit_null 。 これは、Flowがモジュールごとにタイプチェックを有効にする方法に似ています。
  • 既存のコードベースの変換は、最初に-noImplicitNullコンパイラオプションを追加し、次にすべての既存のモジュールに「 @ts :implicit_null」フラグで注釈を付けることによって行われます。 この2番目のステップは簡単に自動化できます。
  • モジュールをインポートするとき、インポートされたモジュールとインポートされたモジュールの暗黙の設定が異なる場合に、タイプを変換する方法に関するポリシーが必要です。

明示的なnullを持つモジュールが暗黙的なnullを持つモジュールをインポートする場合、その最後のポイントにはさまざまなオプションがあります。

  • 極端なアプローチは、 T型のnull可能性を不明として扱い、インポーターが型を?Tまたは!Tに明示的にキャストすることを要求することです。 それには、呼び出し先に多くの注釈が必要になりますが、それは安全です。
  • 別のアプローチは、インポートされたすべてのTタイプを?Tとして扱うことです。 これには、呼び出し元に多くのアノテーションも必要になります。
  • 最後に、インポートされたすべてのTタイプは!Tとして扱うことができます。 もちろん、これは場合によっては間違っていますが、最も実用的なオプションである可能性があります。 型の変数という方法と同様にany型の値に割り当てることができますT

考え?

@ jods4 noImplicitAnyフラグは、既存のコードの意味を_変更しません_。それ以外の場合は暗黙的である何かについて明示的である必要があるだけです。

| コード| フラグ| 意味|
| --- | --- | --- |
| interface Foo { blah; } | | interface Foo { blah:any; } |
| interface Foo { blah; } | noImplicitAny | エラー、明示的なタイプが必要です|
| var foo = 'blah' | | var foo:string = 'blah' |
| var foo = 'blah' | noImplicitNull | var foo:!string = 'blah' |

noImplicitNull 、nullを書き込むことができる変数を作成する前に。 これで、 null書き込むことができない変数ができました。 これは、 noImplicitAnyはまったく異なる獣です。

@RyanCavanaughは、既存のコードのセマンティクスを変更するフラグをすでに除外しています。 TSチームの明示的な要件を完全に無視する場合、このチケットはもう1年間使用できます。

@jesseschalken申し訳ありませんが、違いが
noImplicitAny前に、コードにこれが含まれている可能性があります。
let double = x => x*2;
コンパイルして正常に動作します。 しかし、 noImplicitAnyをオンにすると、コンパイラはxが暗黙的に任意であるというエラーをスローします。 新しいフラグで機能するようにコードを変更する必要があります。
let double = (x: any) => x*2以上でまだlet double = (x: number) => x*2
コンパイラーはエラーを発生させましたが、それでも完全に機能するJSコードを発行することに注意してください(エラーの発行をオフにしない限り)。

nullの状況は、私の意見ではほとんど同じです。 議論のために、新しいフラグでTはnull許容ではなく、 T?またはT | nullはタイプTnullの和集合を表すと仮定しましょう。
あなたが持っていたかもしれない前に:
let foo: string; foo = null;またはlet foo = "X"; foo = nullでさえ、同じようにstringと推測されます。
コンパイルして正常に動作します。 次に、新しいnoImplicitNullフラグをオンにします。 突然、TSは、明示的に宣言されていないものにnullを割り当てることができないことを示すエラーをスローします。 ただし、入力エラーを除いて、コードは_同じ_​​、正しいJSコードを出力します。
フラグを使用して、意図を明示的に示し、コードを変更する必要があります。
string? foo; foo = null;

それで、本当に違いは何ですか? コードは常に正常に出力されており、実行時の動作はまったく変更されていません。 どちらの場合も、タイピングシステムからエラーが発生し、タイプ宣言でより明示的になるようにコードを変更して、エラーを取り除く必要があります。

また、どちらの場合も、strictフラグで記述されたコードを取得し、フラグをオフにしてコンパイルしても、エラーなしで同じように機能します。

@robertknight私の現在の考えに非常に近い。

厳密な非nullタイプを選択していないモジュール/定義の場合、 Tは基本的に次のことを意味するはずです。このタイプのすべての種類のnullエラーをオフにします。 T?に強制しようとすると、互換性の問題が発生する可能性があります。

問題は、今日、一部のTは実際にはヌル可能ではなく、一部はヌル可能であるということです。 検討:

// In a strict module, function len does not accept nulls
function len(x: string): number { return x.length; }
// In a legacy module, some calls to len
let abc: string = "abc";
len(abc);

レガシーモジュールでstringstring?にエイリアスすると、nullの可能性のある変数をnull不可能なパラメーターに渡すため、呼び出しはエラーになります。

@ jods4コメントをもう一度読んでください。 テーブルを見てください。 これ以上はっきりと表現する方法がわかりません。

あなたの例は、明示的に定義置くことによって、あなたの結論に達するために作られたfooへと割り当てをfoo隣同士に。 noImplicitAny場合、発生するエラーは、特に変更が必要なコードに起因するものだけです(意味が変更されていないため、より明示的に表現する必要があるだけです)。 noImplicitNull場合、エラーの原因となったコードはfooへの_assignment_ foo _definition_でした。 foo _の定義の意味を_変更したため、これは非常に重要です。 割り当てと定義は明らかにライブラリ境界の異なる側にある可能性があり、そのためにnoImplicitNullフラグがそのライブラリのパブリックインターフェイスの_意味_を変更しました!

フラグはfooの定義の意味を変更しました。

はい、そうです。 「変数がnullを保持できるかどうかは少しもわかりませんが、気にしない」から「この変数はnullがない」に変更されました。 それが正しかった可能性は50/50であり、そうでない場合は、宣言で意図を正確にする必要があります。 最終的に、結果はnoImplicitAny場合とまったく同じになります。つまり、宣言で意図をより明確にする必要があります。

割り当てと定義は、明らかにライブラリ境界の異なる側にある可能性があります

実際、通常、ライブラリでの宣言と私のコードでの使用。 今:

  1. ライブラリが厳密なnullをオプトインしている場合は、その型を正しく宣言する必要があります。 ライブラリが厳密なnull型を持ち、 xがnull許容ではないと言っている場合、nullを割り当てようとすると_実際に報告されるべきエラーです_。
  2. ライブラリが厳密なnullをオプトインしていない場合、コンパイラはその使用法についてエラーを発生させないはずです。

このフラグ( noImplicitAny )は、コードを調整せずにオンにすることはできません。

私はあなたの主張を理解していますが、私たちは意味コードを_変更_しないと言います。 むしろ、私たちは今日の型システムによって_caturedされていない意味を表現_しています。

これは、今日のコードでエラーをキャッチするので優れているだけでなく、そのような手順を実行しないと、TSで使用可能な非null型は存在しないと言えます。

null許容型ではない場合に朗報です。 TSチームは、TSアップデートに重大な変更を導入することに問題がないようです。
これが表示された場合:
http://blogs.msdn.com/b/typescript/archive/2015/09/02/announcing-typescript-1-6-beta-react-jsx-better-error-checking-and-more.aspx
それらは、新しいファイルタイプで重大な変更(相互に排他的なオプションの構文)を導入し、新しいファイルタイプなしで重大な変更を導入します(すべての人に影響します)。
これは、null許容型ではないタイプ(たとえば、.stsまたは厳密なTypescript拡張子と対応する.sdts)について議論できる前例です。

ここで、コンパイラーが未定義の型をチェックするのか、それともnull型(およびどの構文)をチェックするのかを判断する必要があり、確かな提案があります。

@jbondc非常に興味深い読み物。 オプションのnull不可への移行よりもNNBD(デフォルトではnull不可)への移行が簡単であるという私の直感が研究によって確認されたことを嬉しく思います(移行する変更が1桁少なく、Dartの場合は1- 1000行のコードごとに2つの注釈が必要であり、nullが多いコードでも10以下の変更が必要でした)

ドキュメントの複雑さがnull許容型ではないタイプの複雑さを本当に反映しているかどうかはわかりません。 たとえば、ジェネリックのセクションでは、3種類の正式な型パラメーターについて説明し、実際にはそれらが必要ないことを示しています。 TSでは、 nullは単純に完全に空のレコードの型(プロパティなし、メソッドなし)であり、 {}はルートの非null型であり、null不可能なジェネリックは単純です。 G<T extends {}> -複数の種類の仮型パラメーターについて説明する必要はまったくありません。

さらに、彼らはvar !xような多くの非必須の砂糖を提案しているようです

同じ問題を扱っている既存の言語の調査は、しかし本当の宝石です。

ドキュメントを読んで、 Optional / Maybe型は、特に一般的なコンテキストでは、null許容型よりも強力であることに気付きました。これは主にJust(Nothing)をエンコードできるためです。 たとえば、タイプT値を含み、キーの存在に応じて値を返す場合と返さない場合があるgetをサポートする汎用Mapインターフェイスがある場合:

interface Map<T> {
  get(s:string):Maybe<T>
}

TのタイプがMaybe<U>妨げるものは何もありません。 コードは完全に機能し、キーが存在する場合はJust(Nothing)を返しますが、 Nothing値が含まれ、キーがまったく存在しない場合はNothing返します。

対照的に、null許容型を使用する場合

interface Map<T> {
  get(s:string):T?
}

その場合、 Tがnull可能である場合、欠落しているキーとnull値を区別することは不可能です。

いずれにせよ、null許容値と非null許容値を区別し、それに応じて使用可能なメソッドとプロパティをモデル化する機能は、あらゆる種類の型安全性の前提条件です。

@jbondcこれは非常に

研究によると、宣言の80%が実際には非ヌルを意味していること、またはKLOC(p。21)ごとに20個のヌルアノテーションしかないことがわかっているのは安心です。 ドキュメントに記載されているように、これはデフォルトでnull以外の強力な議論であり、これも私の気持ちでした。

null以外の賛成で別の引数は、それがきれい型システムを作成することです: null 、独自のタイプであり、 T null以外とされるT?同義語ですT | null 。 TSにはすでに共用体タイプがあるため、すべてが素晴らしく、クリーンで、うまく機能します。

その問題に取り組んだ最近の言語のリストを見ると、新しい最新のプログラミング言語がこの長年の問題を処理し、コードベースのnullバグを減らす必要があると本当に思います。 これは、型システムでモデル化する必要があるものにとっては、あまりにも一般的な問題です。 TSがいつかそれを手に入れることを私はまだ望んでいます。

演算子T!のアイデアは興味深く、おそらく役に立つと思いました。 Tがnull以外の型で、 T?T | nullであるシステムを考えていました。 しかし、null入力があってもnull以外の結果を保証する汎用APIを実際に作成できないのは気になりました。 私には良いユースケースはありませんが、理論的にはこれを忠実にモデル化することはできませんでした: function defined(x) { return x || false; }
null以外の反転演算子を使用すると、次のことができます: function defined<T>(x: T): T! | boolean 。 つまり、 definedT defined返す場合、一般的なT制約がnull可能であったとしても、たとえばstring?ようにnull以外であることが保証されます。 そして、私はそれがTSでモデル化するのは難しいとは思わない:指定したT!場合、 T含まれる共用体型であるnullタイプを、除いたタイプを返すnull組合から

@spion

ドキュメントを読んで、オプション/多分型はnull許容型よりも強力であることに気づきました

Maybe構造体をネストすることはできますが、 nullネストすることはできません。

これは、新しいAPIを定義するという文脈では興味深い議論ですが、既存のAPIをマッピングする場合、選択肢はほとんどありません。 言語マップをNullsからMaybeすると、関数が完全に書き直されない限り、その利点を利用できません。

Maybeは、値があるかどうかと値が何であるかという2つの異なる情報をエンコードします。 Map例を見て、C#を見ると、これはDictionary<T,K>から明らかです。
bool TryGet(K key, out T value)
C#にタプル(おそらくC#7)がある場合、これは基本的に次と同じであることに注意してください。
(bool hasKey, T value) TryGet(K key)
これは基本的にMaybeあり、 null保存できます。

JSにはこの問題に対処する独自の方法があり、多くの新しい興味深い問題が発生することに注意してください: undefined 。 通常のJS Mapは、キーが見つからない場合はundefined Mapを返し、それ以外の場合はnullを含むその値を返します。

同じ方法で未定義をモデル化しない限り、問題が解決されないことに気づいていますか?
そうしないと、すべてのnull問題が未定義の問題に置き換えられます(いずれにせよ、このimoの方が一般的です)。

@Griffork

すべてのnull問題は未定義の問題に置き換えられます

いいえ、なぜ彼らはそうするのでしょうか?
nullの問題はなくなり、未定義の問題は残ります。

確かに、 undefinedはまだ問題です。 しかし、それはコーディングスタイルに大きく依存します。 私はundefinedをほとんど使用せずにコーディングしていますが、ブラウザーが強制する場合を除きます。つまり、コードの90%はnullチェックを使用した方が安全です。

ブラウザが強制する場合を除いて、未定義のものはほとんどありません。

JavaScriptは毎ターンundefinedを強制すると思っていたでしょう。

  • 初期化されていない変数。 let x; alert(x);
  • 関数の引数を省略しました。 let foo = (a?) => alert(a); foo();
  • 存在しない配列要素へのアクセス。 let x = []; alert(x[0]);
  • 存在しないオブジェクトプロパティへのアクセス。 let x = {}; alert(x['foo']);

一方、ヌルは、より少なく、より予測可能な状況で発生します。

  • DOMアクセス。 alert(document.getElementById('nonExistent'));
  • サードパーティのWebサービスの応答( JSON.stringify undefined )。 { name: "Joe", address: null }
  • 正規表現。

このため、 nullの使用を禁止し、有線で受信したすべてのnullundefinedに変換し、常にundefinedに対して厳密な等価性チェックを使用します。 これは実際に私たちにとってうまく機能しました。

したがって、 undefined問題がより一般的な問題であることに同意します。

@NoelAbrahamsコーディングスタイル、私はあなたに言います:)

初期化されていない変数

私は常に変数を初期化し、 noImplicitAnyオンにしているので、とにかくlet xはエラーになります。 私がプロジェクトで使用するのに近いのはlet x: any = nullですが、それは私が頻繁に作成することのないコードです。

オプションの関数パラメーター

オプションのパラメーターにはデフォルトのパラメーター値を使用しますが、より理にかなっているようです(コードはパラメーターを読み取り、使用しますね)。 だから私にとって: function f(name?: string = null, options?: any = {})
生のundefinedパラメータ値にアクセスすることは、私にとっては_例外的な_ケースです。

存在しない配列要素へのアクセス

これは私が自分のコードでやらないように努力していることです。 配列の長さが範囲外にならないようにチェックし、スパース配列を使用しません(または、空のスロットをnull0などのデフォルト値で埋めようとします)。
繰り返しますが、私がそうする特別な場合を思いつくかもしれませんが、それは_例外_であり、規則ではありません。

存在しないオブジェクトプロパティへのアクセス。

配列の場合とほとんど同じです。 私のオブジェクトは値が利用できない場合、私はそれを設定し、入力されnullではない、 undefined 。 ここでも、エッジケース(辞書プローブの実行など)が見つかる場合がありますが、それらは_エッジケース_であり、私のコーディングスタイルを代表するものではありません。

undefined返されるすべての_例外的な_ケースでは、 undefined結果に対してすぐにアクションを実行し、それ以上伝播したり、「作業」したりしません。 nullチェックを使用した架空のTSコンパイラの典型的な実例:

let cats: Cat[];
// Note that find returns undefined if there's no cat named Kitty
let myFavoriteCat = cats.find(c => c.name === 'Kitty'); 
if (myFavoriteCat === undefined) {
  // Immediately do something to compensate here:
  // return false; or 
  // myFavoriteCat = new Cat('Kitty'); or
  // whatever makes sense.
}
// Continue with assurance that myFavoriteCat is not null (it was an array of non-nullable cats after all).

このため、nullの使用を禁止し、ネットワーク経由で受信したすべてのnullをundefinedに変換し、undefinedに対して常に厳密な等価性チェックを使用します。 これは実際に私たちにとってうまく機能しました。

このことから、あなたは私とは非常に異なるコーディングスタイルを使用していることがわかります。 基本的にどこでもundefined使用する場合は、はい、静的にチェックされたnullタイプのメリットは私よりもはるかに少なくなります。
だろう。

はい、 undefinedため、100%防水ではありません。この点で、100%正しい、合理的に使用可能な言語を作成できるとは思いません。 JSはあまりにも多くの場所でundefinedを紹介しています。

しかし、上記の私の答えからわかるように、適切なコーディングスタイルを使用すると、ヌルチェックの恩恵を受けることができます。 少なくとも私の意見では、私のコードベースでは、多くの愚かなバグを見つけて防止し、チーム環境の生産性を向上させるのに役立ちます。

@ jods4 、あなたのアプローチを読むのは面白かったです。

私がそのアプローチに反対しなければならないのは、遵守する必要のあるルールがたくさんあるように見えることだと思います。それは共産主義対自由市場のようなものです:smile:

TSチームは、独自のスタイルについて、社内で私たち

@NoelAbrahams "使用して、 undefined " "使用などのルール限りでnull "。

いずれにせよ、一貫性が重要であり、物事がnullまたはundefined (または空の文字列またはゼロ)であるかどうかわからないプロジェクトは望んでいません。 特にTSは現在この問題を解決していないので...

TSにはnullよりもundefinedを優先するルールがあることを知っています。これが任意の「一貫性のため」の選択なのか、それとも選択の背後にさらに多くの議論があるのか​​、興味があります。

私が使用したいのはなぜnullではなくundefined

  1. これは、開発者にとっておなじみの方法で機能します。多くは、C#などの静的オブジェクト指向言語に由来します。
  2. 初期化されていない変数は、通常、多くの言語でコードの臭いと見なされますが、JSで異なる必要がある理由はわかりません。 あなたの意図を明確にしてください。
  3. JSは動的言語ですが、変更されない静的型を使用するとパフォーマンスが向上します。 プロパティをnullよりもdelete nullに設定する方が効率的です。
  4. それは間きれいな違いをサポートnull定義されたが、値がないことを意味しており、 undefined ...定義されていません、。 2つの違いが明らかな場所:オプションのパラメータ。 パラメータを渡さないと、 undefinedます。 コードベースでundefinedを使用する場合、空の値をどのように_pass_しますか? nullを使用すると、ここで問題はありません。
  5. このスレッドで説明されているように、これはnullチェックへのクリーンなパスを提供します。これはundefinedでは実際には実用的ではありません。 多分私はこれについて空想にふけっていますが。
  6. 一貫性を保つために選択する必要があります。IMHO nullundefinedと同じくらい優れています。

nullundefinedを好む理由は
デフォルトの引数とobj.nonexistentProp返す一貫性
undefined

それ以外は、nullと見なされるものを超えてバイクシェディングを取得しません
変数がnull許容であることを要求するのに十分です。

火、2015年9月8日には、午前6時48分jodsは[email protected]書きました:

@NoelAbrahamshttps ://github.com/NoelAbrahams 「未定義を使用」は次のとおりです
「nullを使用する」と同じくらいのルール。

いずれにせよ、一貫性が鍵であり、私がいるプロジェクトは望んでいません
物事がnullまたは未定義(または空)であると想定されているかどうかわからない
文字列またはゼロ)。 特にTSは現在これを支援していないので
問題...

TSにはnullよりもundefinedを優先するルールがあることを知っています。
「一貫性のために」任意の選択であるか、それ以上の場合
選択の背後にある議論。

_I_が未定義ではなくnullを使用する理由:

  1. それは私たちの開発者にとっておなじみの方法で機能し、多くは静的OOから来ています
    C#などの言語。
  2. 初期化されていない変数は通常、コードの臭いと見なされます
    多くの言語、JSでなぜ違うのかわからない。 あなたの
    明確な意図。
  3. JSは動的言語ですが、パフォーマンスは
    変更されない静的タイプ。 プロパティをに設定する方が効率的です
    削除するよりもnull。
  4. 定義されているnull間の明確な違いをサポートしますが
    値がないことを意味し、未定義、つまり...未定義です。
    2つの違いが明らかな場所:オプション
    パラメーター。 パラメータを渡さないと、未定義になります。 どうやって
    コードでundefinedを使用する場合は、空の値を_pass_します。
    ベース? nullを使用しても、ここでは問題はありません。
  5. これで説明されているように、nullチェックへのクリーンパスを提供します
    スレッド。これは、未定義では実際には実用的ではありません。 多分
    私はこれを夢見ています。
  6. あなたは一貫性のために選択をしなければなりません、IMHOnullは同じくらい良いです
    未定義。


このメールに直接返信するか、GitHubで表示してください
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -138514437

@impinball
すべてのgithubディスカッションで「bikeshedding」の使用をやめることはできますか? undefinedまたはnullは、チームの好みであると言っても過言ではありません。 しかし、ヌルチェックにundefinedを含めるかどうか、およびそれがどのように機能するかという問題は簡単ではありません。 そもそも、それがどのようにバイクシェディングなのかわかりませんか?

null許容型ではないTS1.5のフォークを作成しましたが、驚くほど簡単でした。 しかし、公式のTSコンパイラでnull許容型を使用するためのコンセンサスが必要な、2つの難しい問題があると思います。どちらも、明確な結論なしに、上記で詳細に説明されています。

  1. undefined何をしますか? (私の意見:それはまだどこにでもあり、チェックされていません)
  2. 既存のコード、特に定義との互換性をどのように処理しますか? (私の意見:少なくとも定義ファイルごとにオプトインフラグ。null注釈を追加しなければならない可能性があるため、フラグをオンにすると「壊れます」。)

私の個人的な意見は、nullとundefinedは扱われるべきだということです
同等にnull可能性の目的のため。 どちらもそのユースケースに使用されます。
値がないことを表します。 1つは、値が存在しなかったことです。
もう1つは、値がかつて存在し、現在は存在しないということです。 両方
null可能性をカウントする必要があります。 ほとんどのJS関数は未定義を返しますが、多くは
DOMおよびライブラリ関数はnullを返します。 どちらも同じユースケースを提供します。 したがって、
それらは同等に扱われるべきです。

バイクシェディングのリファレンスは、コードスタイルの引数に関するものでした。
値がないことを表すために使用する必要があります。 一部の人は
ただnull、いくつかはちょうど未定義を主張している、そしていくつかは
混合。 この提案は、それらの1つだけに限定されるべきではありません。

火、2015年9月8日には、午前13時28分jodsは[email protected]書きました:

@impinball https://github.com/impinball
すべてのgithubディスカッションで「bikeshedding」の使用をやめることはできますか? 安全にできる
未定義またはnullの使用はチームの好みであると言います。 しかし、問題
未定義をヌルチェックに含めるかどうか(または含めないか)とその方法
それがうまくいくのは簡単ではありません。 だから私はそれがどのようにバイクシェディングであるかわかりません
最初の場所?

null許容型ではないTS1.5のフォークを作成しましたが、
驚くほど簡単です。 しかし、私は2つの難しい問題があると思います
公式のTSコンパイラでnull許容型を使用するには、コンセンサスが必要です。
どちらも明確な結論なしに上記で詳細に議論されています:

  1. 未定義で何をしますか? (私の意見:それはまだどこにでもあります
    未チェック)
  2. 特に既存のコードとの互換性をどのように処理しますか
    定義? (私の意見:少なくとも定義ファイルごとに、オプトインフラグ。
    nullを追加しなければならない可能性があるため、フラグをオンにすることは「破壊的」です
    注釈。)


このメールに直接返信するか、GitHubで表示してください
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -138641395

@ jods4私はあなたのポイント2から、あなたのフォークも明示的なnull許容型の注釈を要求する代わりに、デフォルトでnull許容型ではないと推測していると思いますか?

リンクしてもらえますか? やってみたいです。

@impinball
undefinedに対する(ある程度の)安全性も見たいのですが、それはかなり普及しています。

特に、null許容型ではない型の配列を定義できますか?
範囲外(またはスパース)の配列アクセスがundefined返すとすると、配列を便利に定義して使用できますか?
すべての配列をnull許容にすることは、実際には負担が大きすぎると思います。

null undefinedタイプとT | nullT | undefinedT | null | undefinedそして上記の質問に対する簡単な答えを提供するかもしれません。 しかし、省略構文についてはどうでしょうか。 T?は何の略ですか? nullと未定義の両方? 2つの異なる速記が必要ですか?

@Arnavion
ヌル型と未定義型はすでにTSに存在します。
私の見解は次のとおりです。

  1. すべての型をnull不可にします(推測された型を含む)。
  2. null型に、型宣言で使用できる名前( null )を付けます。
  3. nullからanyへの拡大を削除します;
  4. T | nullと同じ構文の省略形T?を導入します。
  5. nullから他のタイプへの暗黙の変換を削除します。

私の情報源にアクセスできなければ、それがその要点だと思います。 既存のNull型と素晴らしいTS型システム(特に共用体型と型ガード)が残りを行います。

私はまだgithubでの作業をコミットしていないので、今のところリンクを共有できません。 最初に互換性スイッチをコーディングしたかったのですが、他のことに非常に忙しかったです:(
互換性スイッチはもっと複雑です<_ <。 しかし、現在TSコンパイラは多くのエラーでコンパイルされ、多くの既存のテストが失敗するため、これは重要です。
しかし、実際にはまったく新しいコードでうまく機能しているようです。

この会話が円を描いてどのように進んでいるかを説明できることを期待して、これまでに何人かのコメント投稿者から見たものを要約しましょう。
問題:特定の「特殊なケース」の値は、言語内の他のすべてのタイプ(つまり、nullおよび未定義)とは異なる方法で処理されるため、それらを処理するように設計されていないコードに含まれていることを検出しています。
私:これらの問題が起こらないように、プログラミング標準をレイアウトしてみませんか?
その他:意図がタイピングに反映されればいいのですが、タイプスクリプトで作業するすべてのチームによって異なる方法で文書化されることはなく、サードパーティのライブラリに関する誤った仮定が少なくなるためです。
Everone:この問題にどのように対処するつもりですか?
その他:nullをより厳密にし、標準を使用して未定義を処理しましょう!

未定義がnullよりもはるかに問題である場合に、これがどのように解決策と見なされるかわかりません。

免責事項:これはここにいるすべての人の態度を反映しているわけではありません。これを強調したかったのは十分に出てきたということだけです。
これが受け入れられる唯一の方法は、それが問題の解決策である場合であり、問​​題の小さな部分に対する少しの解決策ではありません。

ダンフォン!
*みんな

*それは私がそれを強調したかったので十分に出てきました。

また、最後の段落は「この提案の唯一の方法」と読む必要があります

@ jods4特定の構成に依存すると思います。 場合によっては、次のように、そのような場合にnull可能でないことを保証できます。

declare const list: T![];

for (const entry of list) {
  // `entry` is clearly immutable here.
}

list.forEach(entry => {
  // `entry` is clearly immutable here.
})

list.map(entry => {
  // `entry` is clearly immutable here.
})

この場合、配列が範囲内でチェックされるようにするには、コンパイラーに大量のロジックが必要になります。

declare const list: T![]

for (let i = 0; i < list.length; i++) {
  // This could potentially fail if the compiler doesn't correctly do the static bounds check.
  const entry: T![] = list[i];
}

この場合、コードの一部を実際に評価せずに、コンパイラがentryを取得するためのアクセスが範囲内にあることを確認できることを保証する方法はありません。

declare const list: T![]

const end = round(max, list.length);

for (let i = 0; i < end; i++) {
  const entry: T![] = list[i];
}

簡単で明白なものもありますが、難しいものもあります。

@impinball確かに、 mapforEachfor..ofなどの最新のAPIは、初期化または削除されていない要素をスキップするため、問題ありません。 (これらにはundefinedに設定された要素が含まれていますが、仮想のnullセーフTSはそれを禁止します。)

しかし、従来のアレイアクセスは重要なシナリオであり、そのための優れたソリューションが必要です。 あなたが提案したように複雑な分析を行うことは、些細な場合(一般的であるため重要な場合)を除いて明らかに不可能です。 ただし、 i < array.lengthであることを証明できたとしても、要素が初期化されていることを証明するものではないことに注意してください。

次の例を考えてみましょう。TSは何をすべきだと思いますか?

let array: T![] = [];  // an empty array of non-null, non-undefined T
// blah blah
array[4] = new T();  // Fine for array[4], but it means array[0..3] are undefined, is that OK?
// blah blah
let i = 2;
// Note that we could have an array bounds guard
if (i < array.length) {
  let t = array[i];  // Inferred type would be T!, but this is actually undefined :(
}

Object.definePropertyにも問題があります。

let array = new Array(5)
Object.defineProperty(array, "length", 2, {
  get() { return 10 },
})

水、2015年9月9日には、午前17時49分jodsは[email protected]書きました:

@impinball https://github.com/impinball確かに、マップなどの最新のAPI、
forEachまたはfor..ofは、決してなかった要素をスキップするため、問題ありません。
初期化または削除されました。 (これらには、に設定されている要素が含まれています
未定義ですが、仮想のnullセーフTSはそれを禁止します。)

しかし、古典的なアレイアクセスは重要なシナリオであり、私は
そのための良い解決策を参照してください。 あなたが提案したように複雑な分析を行うことは
些細な場合を除いて明らかに不可能です(それ以来重要な場合
それらは一般的です)。 ただし、i <であることを証明できたとしても注意してください。
array.length要素が初期化されていることを証明するものではありません。

次の例を考えてみましょう。TSは何をすべきだと思いますか?

配列をしましょう:T![] = []; // nullでも未定義でもない、T // blahblahの空の配列
array [4] = new T(); // array [4]には問題ありませんが、array [0..3]が未定義であることを意味します、それで問題ありませんか?// blah blahlet i = 2; //配列境界guardif(i <array)を持つことができることに注意してください。長さ) {
t = array [i]; //推測されるタイプはT!ですが、これは実際には未定義です:(
}


このメールに直接返信するか、GitHubで表示してください
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -139055786

あなたがする必要があるのは次のことです:

  1. Null型とUndefined型を参照可能にします。
  2. nullおよび未定義の値がすべてに割り当てられるのを防ぎます。
  3. null可能性が暗示されている場合は、Nullおよび/またはUndefinedの共用体型を使用します。

それは確かに大胆な破壊的な変化であり、言語により適しているように見えます
拡大。
2015年9月10日午前9時13分、「IsiahMeadows」 [email protected]は次のように書いています。

Object.definePropertyにも問題があります。

let array = new Array(5)
Object.defineProperty(array, "length", 2, {
get() { return 10 },
})

水、2015年9月9日には、午前17時49分jodsは[email protected]書きました:

@impinball https://github.com/impinball確かに、次のような最新のAPI
地図、
forEachまたはfor..ofは、決してなかった要素をスキップするため、問題ありません。
初期化または削除されました。 (これらには、に設定されている要素が含まれています
未定義ですが、仮想のnullセーフTSはそれを禁止します。)

しかし、古典的なアレイアクセスは重要なシナリオであり、私は
そのための良い解決策を参照してください。 あなたが提案したように複雑な分析を行うことは
些細な場合を除いて明らかに不可能です(それ以来重要な場合
それらは一般的です)。 ただし、i <であることを証明できたとしても注意してください。
array.length要素が初期化されていることを証明するものではありません。

次の例を考えてみましょう。TSは何をすべきだと思いますか?

配列をしましょう:T![] = []; // nullでも、未定義でもないTの空の配列//
何とか何とか
array [4] = new T(); // array [4]には問題ありませんが、array [0..3]は
未定義ですが、大丈夫ですか?// blah blahlet i = 2; //
配列境界guardif(i <array.length){
t = array [i]; //推測されるタイプはT!ですが、これは実際には
未定義 :(
}


このメールに直接返信するか、GitHubで表示してください
<<
https://github.com/Microsoft/TypeScript/issues/185#issuecomment-139055786>


このメールに直接返信するか、GitHubで表示してください
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -139230568

@impinball
あなたの例については大丈夫だと思います。 このようにdefinePropertyを使用することは、TSセーフティボックスの外に出て、動的JS領域に足を踏み入れることです。 TSコードでdefineProperty直接呼び出したことはないと思います。

@ aleksey-bykov

言語拡張に適しているように見えます。

この提案のもう1つの問題は、広く受け入れられない限り、価値が低くなることです。
最終的には、更新されたTS定義が必要になりますが、それがめったに使用されない、互換性のない、拡張機能、またはTSのフォークである場合は発生しないと思います。

IIRCがスローするので、型付き配列には保証があることを私は知っています。
範囲外の配列のロードとストアに関するReferenceError。 通常の配列と
インデックスが範囲外の場合、引数オブジェクトは未定義を返します。 私の中で
意見、それはJS言語の欠陥ですが、それを修正することは間違いなく
Webを壊します。

これを「修正」する唯一の方法は、ES6で、プロキシを返すコンストラクタを介して、
そして、そのインスタンスプロトタイプとセルフプロトタイプが元のアレイに設定されました
コンストラクタ。 このようなもの:

Array = (function (A) {
  "use strict";
  function check(target, prop) {
    const i = +prop;
    if (prop != i) return target[prop];
    if (i >= target.length) {
      throw new ReferenceError();
    }
    return i;
  }

  function Array(...args) {
    return new Proxy(new Array(...args), {
      get(target, prop) {
        return target[check(target, prop)];
      },
      set(target, prop, value) {
        return target[check(target, prop)] = value;
      },
    });
  }

  Array.prototype = A.prototype;
  Array.prototype.constructor = Array
  Object.setPrototypeOf(Array, A);
  return Array;
})(Array);

(注:テストされておらず、電話で入力されています...)

木、2015年9月10日には、午前10時09 jodsは[email protected]書きました:

@impinball https://github.com/impinball
あなたの例については大丈夫だと思います。 このようにdefinePropertyを使用すると、
TSセーフティボックスの外に出て、動的JS領域に足を踏み入れないでください。
あなたは考える? TSコードでdefinePropertyを直接呼び出したことはないと思います。

@ aleksey-bykov https://github.com/aleksey-bykov

言語拡張に適しているように見えます。

この提案のもう1つの問題は、広く受け入れられない限り、
価値が低くなります。
最終的には、更新されたTS定義が必要になりますが、そうなるとは思いません。
それがめったに使用されない、互換性のない、拡張機能またはTSのフォークである場合に発生します。


このメールに直接返信するか、GitHubで表示してください
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -139245706

@impinballそもそも「修正」するものがあるかどうかわからない...
これらはJSのセマンティクスであり、TSは何らかの形でそれらに対応する必要があります。

スパース配列と非スパース配列を宣言するための(わずかに異なる)マークアップがあり、非スパース配列を自動的に初期化する場合はどうなりますか(プログラマーは初期化の値または方程式を提供できる可能性があります)。 このようにして、スパース配列を強制的にタイプT|undefined (for ... ofおよびその他の「安全な」操作を使用してタイプTに変更)にし、非スパース配列のタイプをそのままにしておくことができます。

//not-sparse
var array = [arrlength] => index*3;
var array = <number[]>[3];
//sparse
var array = [];

明らかに、これは最終的な構文ではありません。
2番目の例では、すべての値をコンパイラーに初期化します-そのタイプのデフォルト。
これは、スパースでない配列の場合、それらを入力する必要があることを意味します。そうしないと、完全に初期化された後でそれらを何かにキャストする必要があると思います。
また、プログラマーが配列を非スパースにキャストできるように、配列には非スパース型が必要です。

@Griffork
わかりません...混乱します。

このようにして、スパース配列を強制的にタイプT|undefinedすることができます(これは、for ... ofおよびその他の「安全な」操作を使用してタイプTに変更されます)

JSの癖のため、そのようには機能しません。 let arr: (T|undefined)[]と仮定します。
だから私は自由にやることができます: arr[0] = undefined
そうすると、それらの「安全な」関数を使用すると、最初のスロットにundefined返されます。 そうでarr.forEach(x => ...)あなたが言うことができないx: T 。 それでもx: T|undefinedます。

2番目の例では、すべての値をコンパイラーに初期化します-そのタイプのデフォルト。

これは精神的にはTSのようなものではありません。 私は間違っているかもしれませんが、TSの哲学は、型はJSの上の追加のレイヤーにすぎず、codegenには影響を与えないということだと思います。 これは、部分的な型の正確さのために、私があまり好きではないパフォーマンスに影響を及ぼします。

TSは明らかにJSのすべてからユーザーを保護することはできず、有効なTSから呼び出すことができるいくつかの関数/構造があります。これらは、オブジェクトのランタイムタイプに動的な影響を与え、静的なTSタイプ分析を中断します。

これが型システムの穴だったら悪いのでしょうか? つまり、このコードは実際には一般的ではありません。 let x: number[] = []; x[3] = 0;あり、それが必要な場合は、配列let x: number?[]を宣言する必要があります。

完璧ではありませんが、実際のほとんどの使用には十分だと思います。 あなたがサウンドタイプのシステムを望んでいる純粋主義者なら、TSタイプのシステムはとにかくサウンドではないので、あなたは確かに別の言語を見る必要があります。 どう思いますか?

そのため、非スパース配列にキャストする機能も必要だと言いました
タイプするので、パフォーマンスなしで自分で配列を初期化できます
影響。
私は(何を意味するのか)まばらなものを区別するだけで解決します
タイプ別の配列および非スパース配列。

なぜこれが重要なのかわからない人にとっては、それはあなたと同じ理由です
TT|null違いが必要になります。

9:11には、金、2015年9月11日jods [email protected]書きました:

@Griffork https://github.com/Griffork
わかりません...混乱します。

そうすれば、スパース配列をT | undefined型にすることができます(これにより、
for ... ofおよびその他の「安全な」操作を使用してタイプTに変更します)

JSの癖のため、そのようには機能しません。 arrをしましょう:
(T |未定義)[]。
だから私は自由にやることができます:arr [0] = undefined。
そうすると、それらの「安全な」関数を使用すると、未定義が返されます
最初のスロット用。 したがって、arr.forEach(x => ...)では、x:Tとは言えません。
それでもx:T | undefinedである必要があります。

2番目の例では、すべての値をコンパイラに初期化します-デフォルト
そのタイプのために。

これは精神的にはTSのようなものではありません。 多分私は間違っていますが、それは私には思えます
TSの哲学は、タイプはJSの上に追加されたレイヤーにすぎないということです
そしてそれらはcodegenに影響を与えません。 これは、パフォーマンスに影響を及ぼします。
私があまり好きではない部分的なタイプの正確さ。

TSは明らかにJSのすべてからあなたを保護することはできません。
有効なTSから呼び出すことができるいくつかの関数/構造。
オブジェクトのランタイムタイプへの動的な影響と静的TSの中断
タイプ分析。

これが型システムの穴だったら悪いのでしょうか? つまり、このコード
本当に一般的ではありません:let x:number [] = []; x [3] = 0; そしてそれが
あなたがやりたいことのようなものなら、多分あなたはあなたの配列を宣言するべきです
x:番号?[]。

完璧ではありませんが、実際のほとんどの使用には十分だと思います。
あなたがサウンドタイプのシステムを望んでいる純粋主義者なら、あなたは確かにすべきです
TS型システムはとにかく健全ではないので、別の言語を見てください。 何
あなたは思いますか?


このメールに直接返信するか、GitHubで表示してください
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -139408240

@ jods4 「修正」とは、私見のJS言語設計の欠陥を「修正」することでした。 TypeScriptではなく、_JavaScript自体_にあります。

@jods私はTSではなくJSについて不平を言っています。 私はそれが話題から外れていることを認めます。

木、2015年9月10日には、19:19 Griffork [email protected]書きました:

そのため、非スパース配列にキャストする機能も必要だと言いました
タイプするので、パフォーマンスなしで自分で配列を初期化できます
影響。
私は(何を意味するのか)まばらなものを区別するだけで解決します
タイプ別の配列および非スパース配列。

なぜこれが重要なのかわからない人にとっては、それはあなたと同じ理由です
TT|null違いが必要になります。

9:11には、金、2015年9月11日jods [email protected]書きました:

@Griffork https://github.com/Griffork
わかりません...混乱します。

そうすれば、スパース配列をT | undefined型にすることができます(これにより、
for ... ofおよびその他の「安全な」操作を使用してタイプTに変更します)

JSの癖のため、そのようには機能しません。 arrをしましょう:
(T |未定義)[]。
だから私は自由にやることができます:arr [0] = undefined。
そうすると、それらの「安全な」関数を使用すると、未定義が返されます
最初のスロット用。 したがって、arr.forEach(x => ...)では、x:Tとは言えません。
それでもx:T | undefinedである必要があります。

2番目の例では、すべての値をコンパイラに初期化します-デフォルト
そのタイプのために。

これは精神的にはTSのようなものではありません。 多分私は間違っていますが、それは私には思えます
TSの哲学は、タイプはその上に追加されたレイヤーにすぎないというものです。
JS
そしてそれらはcodegenに影響を与えません。 これは、パフォーマンスに影響を及ぼします。
私があまり好きではない部分的なタイプの正確さ。

TSは明らかにJSのすべてからあなたを保護することはできません。
有効なTSから呼び出すことができるいくつかの関数/構造。
持ってる
オブジェクトのランタイムタイプへの動的な影響と静的TSの中断
タイプ分析。

これが型システムの穴だったら悪いのでしょうか? つまり、このコード
本当に一般的ではありません:let x:number [] = []; x [3] = 0; そしてそれが
あなたがやりたいことのようなものなら、多分あなたはあなたの配列を宣言するべきです
させて
x:番号?[]。

完璧ではありませんが、実際のほとんどの使用には十分だと思います。
あなたがサウンドタイプのシステムを望んでいる純粋主義者なら、あなたはすべきです
もちろんです
TS型システムはとにかく健全ではないので、別の言語を見てください。

あなたは思いますか?


このメールに直接返信するか、GitHubで表示してください
<<
https://github.com/Microsoft/TypeScript/issues/185#issuecomment-139408240>


このメールに直接返信するか、GitHubで表示してください
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -139409349

また、配列の長さに関する私のステートメントについては、すべての配列アクセスが範囲内にあり、インターフェイスで明示的に指定されていない限り、範囲外のアクセスは定義されていないという前提で操作できます。 これはC / C ++と非常によく似ており、言語仕様を使用するサードパーティのコンパイラを最終的に作成することを決定した場合でも、タイピングの改善と、コンパイラの最適化の負荷の両方が可能になりますが、それほど心配する必要はありません。放出に一致します。

一致するC / C ++の未定義動作をサポートすることは、表面上は非常にばかげているように聞こえますが、この場合、それだけの価値があると思います。 範囲外のアクセスを行うことによって実際に「より良い」ことが行われることはめったにありません。 私が見た使用の99.99%は、非常に刺激的なコードの臭いであり、ほとんどの場合、JavaScriptにほとんど精通していない人々によって行われます。

(私の経験では、これらの人々のほとんどは、CoffeeScript、ましてやTypeScriptについてさえ聞いたことがありません。彼らの多くは、完成して標準化されたばかりのJSの新しいバージョンであるES2015にさえ気づいていません。)

これに対する新たな解決策はありますか?

null許容型ではない場合でも、_assuredly_ nullである変数のプロパティにアクセスしようとすると、TypeScriptが失敗するのは便利なようです。

var o = null;
console.log(o.x);

...失敗するはずです。

型システムを介して、すべての配列アクセスが境界チェックされていることを確認することは、依存型の領域に流れ込んでいるように見えます。 依存型はかなりきちんとしていますが、それはnull許容型ではないタイプよりもはるかに大きな機能のようです。

コンパイル時に境界チェックが配列に適用されないと仮定すると、3つのオプションがあるようです。

  1. 配列のインデックス付け(およびインデックスによる任意の配列要素へのアクセス)は、null許容型ではない配列であっても、null許容型を返すと見なされます。 基本的に、 [] 「メソッド」の型アノテーションはT?です。 境界チェックのインデックス作成のみを行っていることがわかっている場合は、アプリケーションコードでT?T!にキャストできます。
  2. 配列のインデックス付けは、配列のジェネリック型パラメーターとまったく同じ型(同じnull可能性)を返し、すべての配列アクセスがアプリケーションによって境界チェックされていると想定されます。 範囲外のアクセスはundefinedを返し、タイプチェッカーによってキャッチされません。
  3. 核となるオプション:すべての配列はnull許容として言語にハードコーディングされており、null許容でない配列を使用しようとするとタイプチェックに失敗します。

これらはすべて、オブジェクトのプロパティへのインデックスベースのアクセスにも適用されます。たとえば、 object['property']場合、 objectのタイプは{ [ key: string ]: T! }です。

個人的には、配列またはオブジェクトへのインデックス付けがnull許容型を返す最初のオプションを好みます。 しかし、2番目のオプションでさえ、すべてがnull許容であるよりも優れているように見えます。これは、現在の状況です。 核オプションはグロスですが、正直なところ、すべてがnull可能であるよりも優れています。

タイプをデフォルトでnull許容にするべきか、デフォルトでnull許容にするべきかという2番目の質問があります。 どちらの場合でも、ジェネリックスを処理するために、明示的にnull許容型と明示的に非null許容型の両方の構文を使用すると便利なようです。 たとえば、コンテナクラス(Mapなど)のgetメソッドが、ある値を取り、場合によっては型を返すことを想像してください。_コンテナにnull許容値以外の値しか含まれていなくても_:

class Container<K,V> {
  get(key: K): V? {
    // fetch from some internal data structure and return the value, if it exists
    // return null otherwise
  }
}

// only non-nullable values allowed in the container
const container = new Container<SomeKeyClass!, SomeValueClass!>();
const val: SomeValueClass!;
// ... later, we attempt to read from the container with a get() call
// even though only non-nullables are allowed in the container, the following should fail:
// get() explicitly returns null when the item can't be found
val = container.get(someKey);

同様に、null許容キータイプを使用している場合でも、コンテナクラスが挿入時にnull以外のキーのみを受け入れるようにしたい場合があります(これはそれほど強力な議論ではありません)。

class Container<K, V> {
  insert(key: K!, val: V): void {
    // put the val in the data structure
    // the key must not be null here, even if K is elsewhere a nullable type
  }
}

const container = new Container<SomeKeyClass?, SomeValueClass>();
container.insert(null, new SomeValueClass()); // fails

したがって、デフォルトが変更されるかどうかに関係なく、null許容型と非null許容型の両方に明示的な構文を設定すると便利なようです。 私が何かを逃していない限り?

両方の構文がある時点では、デフォルトは--noImplicitAnyようなコンパイラフラグである可能性があるようです。 個人的には、2.0リリースまでデフォルトのままであることに投票しますが、エスケープハッチがある限り(少なくとも一時的に)どちらでも問題ないようです。

(TSタイピングの領域で)範囲外のアクセスが未定義の動作になるとしても、2番目のオプションをお勧めします。これは、これに対する適切な妥協点だと思います。 速度が大幅に向上し、処理が簡単になります。 範囲外のアクセスが可能であると本当に期待している場合は、null許容型を明示的に使用するか、結果をnull許容型にキャストする必要があります(これは常に可能です)。 そして一般的に、配列がnull許容でない場合、範囲外のアクセスはほとんどの場合バグであり、ある時点で激しく発生するはずです(これはJSの欠陥です)。

プログラマーは、彼らが期待していることを明確にする必要があります。 型安全性は劣りますが、この場合、型安全性が邪魔になる可能性があると思います。

例として合計関数を使用した、各オプションとの比較を次に示します(プリミティブが最も問題があります)。

// Option 1
function sum(numbers: !number[]) {
  let res = 0
  for (let i = 0; i < numbers.length; i++) {
    res += <!number> numbers[i]
  }
  return res
}

// Option 2
function sum(numbers: !number[]) {
  let res = 0
  for (let i = 0; i < numbers.length; i++) {
    res += numbers[i]
  }
  return res
}

// Option 3
function sum(numbers: number[]) {
  let res = 0
  for (let i = 0; i < numbers.length; i++) {
    res += <!number> numbers[i]
  }
  return res
}

別の例: map関数。

// Option 1
function map<T>(list: !T[], f: (value: !T, index: !number) => !T): !T[] {
  let res: !T[] = []
  for (let i = 0; i < list.length; i++) {
    res.push(f(<!T> list[i], i));
  }
  return res
}

// Option 2
function map<T>(list: !T[], f: (value: !T, index: !number) => !T): !T[] {
  let res: !T[] = []
  for (let i = 0; i < list.length; i++) {
    res.push(f(list[i], i));
  }
  return res
}

// Option 3
function map<T>(list: T[], f: (value: !T, index: !number) => !T): T[] {
  let res: T[] = []
  for (let i = 0; i < list.length; i++) {
    const entry = list[i]
    if (entry !== undefined) {
      res.push(f(<!T> entry, i));
    }
  }
  return res
}

別の質問:これらのそれぞれのentryのタイプは何ですか? !string?string 、またはstring

declare const regularStrings: string[];
declare const nullableStrings: ?string[];
declare const nonnullableStrings: !string[];

for (const entry of regularStrings) { /* ... */  }
for (const entry of nullableStrings) { /* ... */  }
for (const entry of nonnullableStrings) { /* ... */  }

オプション3は、ちょっとした提案でした:stuck_out_tongue:

Re:あなたの最後の質問:

declare const regularStrings: string[];
declare const nullableStrings: string?[];
declare const nonNullableStrings: string![]; // fails typecheck in option three

for(const entry of regularStrings) {
  // option 1: entry is of type string?
  // option 2: depends on default nullability
}

for(const entry of nullableStrings) {
  // option 1 and 2: entry is of type string?
}

for(const entry of nonNullableStrings) {
  // option 1: entry is of type string?
  // option 2: entry is of type string!
}

場合によっては(たとえば、null許容型ではない型を返したいが、配列から取得している場合)、未定義の値がないことが他の場所で保証されていると仮定して、オプション1で追加のキャストを行う必要があります。配列内(この保証の要件は、どのアプローチを採用しても変更されません。 as string!と入力するだけで済みます)。 個人的には、より明示的であり(暗黙的に発生するのではなく、危険な可能性のある動作を行うタイミングを指定する必要があります)、ほとんどのコンテナクラスの動作と一貫性があるため、私はまだそれを好みます。たとえば、マップのget関数は明らかにnull許容型を返します(キーの下に存在する場合はオブジェクトを返し、存在しない場合はnullを返します)。 Map.prototype.getがnull許容型を返す場合、 object['property']はおそらくそれらはnull可能性について同様の保証を行い、同様に使用されるため、同じです。 これにより、配列は奇数のままになり、null参照エラーが戻ってくる可能性があり、型システムによってランダムアクセスがnull不可になることが許可されます。

間違いなく他のアプローチがあります。 たとえば、現在Flowはオプション2を使用しており、最後に、 SoundScriptがスパース配列を仕様で明示的に違法にしたことを確認

パフォーマンスの側面は、現時点では非常に理論的だと思います。AFAIKTypeScriptは、ここでの選択に関係なく同じJSを発行し続け、基盤となるVMは、内部で境界チェックアレイを継続するためです。 だから私はその議論にあまり動揺していません。 私の頭に浮かぶ質問は、主に利便性と安全性に関するものです。 私には、ここでの安全性の勝利は、利便性のトレードオフの価値があるように思われます。 もちろん、どちらもすべての型をnull許容にするよりも改善されています。

パフォーマンスの部分はほとんど理論的なものであることに同意しますが、それでも
仮定の便利さのように。 ほとんどの配列は高密度であり、
デフォルトは、ブール配列と数値配列には意味がありません。 そうでない場合
密な配列を意図しているため、明示的にnull許容としてマークする必要があります。
意図は明らかです。


TypeScriptは本当に物事を主張する方法を必要とします、なぜなら主張はしばしば
他の言語での静的型チェックを支援するために使用されます。 私はで見ました
V8コードベースはUNREACHABLE();マクロであり、仮定を次のようにすることができます。
もう少し安全に、不変条件に違反した場合にプログラムをクラッシュさせます。 C ++
タイプチェックを支援する静的アサーション用のstatic_assertがあります。

2015年10月20日火曜日午前4時1分、マットベイカー[email protected]
書きました:

オプション3は、ちょっとした提案でした[画像:
:stuck_out_tongue:]

Re:あなたの最後の質問:

const regularStringsを宣言します:string []; const nullableStringsを宣言します:string?[]; const nonNullableStringsを宣言します:string![]; //オプション3のタイプチェックに失敗します
for(regularStringsの定数エントリ){
//オプション1:エントリは文字列型ですか?
//オプション2:デフォルトのnull可能性に依存
}
for(nullableStringsの定数エントリ){
//オプション1および2:エントリは文字列型ですか?
}
for(nonNullableStringsの定数エントリ){
//オプション1:エントリは文字列型ですか?
//オプション2:エントリは文字列型です!
}

場合によっては— null許容型ではない型を返したいが、
たとえば、配列から取得します—追加のキャストを行う必要があります
オプション1では、未定義がないことが他の場所で保証されていることを前提としています
配列内の値(この保証の要件は変更されません
どのアプローチを採用するかに関係なく、文字列として入力するだけで済みます!)。
個人的には、どちらもより明確であるため、私はまだそれを好みます(あなたは
それとは対照的に、危険な可能性のある行動をとっている時期を指定する
暗黙的に発生します)そしてほとんどのコンテナクラスがどのように一貫しているか
動作:たとえば、Mapのget関数は明らかにnull許容型を返します
(キーの下にオブジェクトが存在する場合はオブジェクトを返し、存在しない場合はnullを返します)、
Map.prototype.getがnullableを返す場合、object ['property']
彼らはについて同様の保証をするので、おそらく同じことをする必要があります
null可能性と同様に使用されます。 これは、配列を奇妙なものとして残します
null参照エラーが忍び寄る可能性がある場所、およびランダムアクセスが存在する場所
型システムによってnull許容不可にすることができます。

間違いなく他のアプローチがあります。 たとえば、現在Flowは
オプション2http://flowtype.org/docs/nullable-types.html 、最後のI
チェックされたSoundScriptは、スパース配列を仕様で明示的に違法にしました
https://github.com/rwaldron/tc39-notes/blob/master/es6/2015-01/JSExperimentalDirections.pdf
(まあ、強力なモード/「SaneScript」はそれらを違法にします、そしてSoundScriptは
新しいルールのスーパーセット)、これはある程度問題を回避します
手動の長さに対処する方法を理解する必要がありますが
変更と初期割り当て。

現時点では、パフォーマンスの側面は非常に理論的だと思います。
AFAIK TypeScriptは、関係なく同じJSを発行し続けるため
ここで任意の選択肢をキャストすると、基盤となるVMが続行されます
境界に関係なく、内部の配列をチェックします。 だから私はあまり動揺していません
その議論。 私の頭に浮かぶ質問は、主に利便性と
安全性; 私には、ここでの安全性の勝利は、利便性のトレードオフの価値があるように思われます。 の
もちろん、どちらもすべての型をnull許容にするよりも改善されています。


このメールに直接返信するか、GitHubで表示してください
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -149468527

イシアメドウズ

それらをnon-voidタイプと呼び始めるべきでしょうか? いずれにせよ、 non-voidT!または!Tで明示的に定義するのは間違いだと思います。 人間として読むことは難しく、TypeScriptコンパイラのすべてのケースに対処することも困難です。

デフォルトでnon-voidタイプで見られる主な問題は、それが重大な変更であるということです。 フローのような静的分析を追加するだけで、動作はまったく変更されませんが、より多くのバグが検出されるとしたらどうでしょうか。 そうすれば、このクラスのバグのほとんどを今すぐキャッチできますが、構文を変更しないでください。将来的には、重大な変更ではないコンパイラフラグまたはデフォルトの動作を導入する方がはるかに簡単になります。

`` `.ts
//関数はうまくコンパイルされます
関数len(x:文字列):数値{
x.lengthを返します。
}

len( "works"); // 5
len(null); //エラー、nullのプロパティ長はありません

``` .ts
function len(x: string): number {
    if (x === null) {
        return -1;
    }
    return x.length;
}

len("works"); // 5
len(null); // null

ここで実際に行われるのは、入力データをnon-voidとしてモデル化することですが、関数で処理されるときにvoid暗黙的に追加します。 同様に、明示的にnullまたはundefined返すことができない限り、戻り型はnon-void undefined

?TまたはT?タイプを追加することもできます。これにより、使用前にnull(および/または未定義)チェックが強制されます。 個人的にはT?が好きですが、Flowで?Tを使用する前例があります。

`` `.ts
関数len(x:?string):数値{
x.lengthを返します。 //エラー:タイプ?stringに長さプロパティがありません。タイプガードを使用する必要があります
}

One more example -- what about using function results?

``` .js
function len(x: string): number {
    return x.length;
}

function identity(f: string): string {
    return f;
}

function unknown(): string {
    if (Math.random() > 0.5) {
        return null;
    }
    return "maybe";
}

len("works"); // 5
len(null); // error, no property length of null

identity("works"); // "works": string
identity(null); // null: void
unknown(); // ?string

len(identity("works")); // 5
len(identity(null)); // error, no property length of null
len(unknown()); // error: no length property on type ?string, you must use a type guard

内部的には、ここで実際に行われていることは、TypeScriptがnullを処理するかどうかを確認することで、型がnullになる可能性があるかどうかを推測し、nullの可能性のある値が与えられることです。

ここでの唯一のトリッキーな部分は、定義ファイルとのインターフェース方法です。 これは、2番目の例のように、デフォルトでfunction(t: T)宣言する定義ファイルがnull / voidチェックを行うと想定することで解決できると思います。 これは、これらの関数が、コンパイラーがエラーを生成することなくnull値をとることができることを意味します。

これにより、次の2つのことが可能になります。

  1. ?T型の構文が徐々に採用され、オプションのパラメーターはすでに交換されています。
  2. 将来的には、コンパイラフラグ--noImplicitVoidが追加される可能性があります。これにより、宣言ファイルはコンパイルされたコードファイルと同じように扱われます。 これは「壊れる」ことですが、はるか先で行われる場合、タイプがvoidおよびTなる可能性がある場合、大多数のライブラリは?Tを使用するベストプラクティスを採用します。できないとき。 また、オプトインになるため、使用することを選択した人だけが影響を受けます。 これには、オブジェクトが無効になる可能性がある場合に、 ?T構文を使用する必要がある場合もあります。

これは現実的なアプローチだと思います。TypeScriptでソースが利用できる場合に安全性が大幅に向上し、これらのトリッキーな問題を見つけながら、定義ファイルとの簡単で、かなり直感的で、下位互換性のある統合を可能にします。

プレフィックス?バリアントは、クロージャコンパイラアノテーション、IIRCでも使用されます。

火、2015年11月17日には、午前13時37トムジャックの[email protected]書きました:

それらを非void型と呼び始めるべきでしょうか? とにかく、
T!でnon-voidを明示的に定義します。 または!Tは間違いです。 難しい
人間として読むこと、そしてまたすべての場合に対処することは難しい
TypeScriptコンパイラ。

デフォルトで非void型で見られる主な問題は、それが
変化を壊します。 静的分析を追加するとどうなるでしょうか。
フローと同様に、動作はまったく変更されませんが、キャッチされます
もっとバグ? そうすれば、このクラスのバグのほとんどを今すぐキャッチできますが、
構文を変更しないでください。将来的には、構文を変更する方がはるかに簡単になります。
コンパイラフラグまたはデフォルトの動作を導入します。
変化する。

//関数は幸せにコンパイルしますfunctionlen(x:string):number {
x.lengthを返します。
}

len( "works"); // 5
len(null); //エラー、nullのプロパティ長はありません

関数len(x:文字列):数値{
if(x === null){
-1を返します。
}
x.lengthを返します。
}

len( "works"); // 5
len(null); // ヌル

ここで実際に行われるのは、入力データを非voidとしてモデル化することです。
ただし、関数で処理されるときにvoidを暗黙的に追加します。 同様に、
nullまたは明示的にnullを返すことができない限り、戻り型はvoidではありません。
未定義

「T」または「T」を追加することもできます。 タイプ。これはnull(および/または
未定義)使用前に確認してください。 個人的にはTが好きですか?でも前例があります
フローで?Tを使用します。

関数len(x:?string):数値{
x.lengthを返します。 //エラー:タイプ?stringに長さプロパティがありません。タイプガードを使用する必要があります
}

もう1つの例-関数の結果を使用するのはどうですか?

関数len(x:文字列):数値{
x.lengthを返します。
}
関数の単位元(f:文字列):文字列{
fを返す;
}
関数unknown():文字列{
if(Math.random()> 0.5){
nullを返します。
}
「たぶん」を返す;
}

len( "works"); // 5
len(null); //エラー、nullのプロパティ長はありません

アイデンティティ( "作品"); //「動作」:文字列
アイデンティティ(null); // null:void
わからない(); // ?ストリング

len(identity( "works")); // 5
len(identity(null)); //エラー、nullのプロパティ長はありません
len(unknown()); //エラー:タイプ?stringに長さプロパティがありません。タイプガードを使用する必要があります

内部的には、ここで実際に起こっているのは、TypeScriptが
型が処理するかどうかを確認することにより、型がnullになる可能性があるかどうかを推測する
nullであり、nullの可能性のある値が与えられます。

ここでの唯一のトリッキーな部分は、定義ファイルとのインターフェース方法です。 私
これは、デフォルトで次のように仮定することで解決できると思います。
関数(t:T)を宣言する定義ファイルはnull / voidチェックを行います
2番目の例のように。 これは、それらの機能が取ることができることを意味します
コンパイラがエラーを生成せずにnull値。

これにより、次の2つのことが可能になります。

  1. オプションの?T型構文の段階的な採用
    パラメータはすでににスワップされています。
  2. 将来的には、コンパイラフラグ--noImplicitVoidが追加される可能性があります
    これは、宣言ファイルをコンパイルされたコードファイルと同じように扱います。 この
    「壊れる」でしょうが、はるか先で行われた場合、大部分の
    ライブラリは、タイプが可能な場合に?Tを使用するベストプラクティスを採用します
    無効になり、できない場合はTになります。 また、オプトインになるので、それらだけ
    それを使用することを選択した人は影響を受けます。

安全性が大幅に向上するため、これは現実的なアプローチだと思います
ソースがTypeScriptで利用可能である場合、それらのトリッキーな問題を見つけ、
それでも、簡単で、かなり直感的で、下位互換性があります
定義ファイルとの統合。


このメールに直接返信するか、GitHubで表示してください
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -157463734

いい視点ね。 また、既存のオプションのパラメーター宣言と多くの類似点があります。

`` `.ts
インターフェースwithOptionalProperty {
o ?:文字列
}
インターフェイスwithVoidableProperty {
o:?string
}

function withOptionalParam(o?:string){}
関数withVoidableParam(o:?string){}
`` `

実際には、プレフィックスを使用します。 それらは、明示的なnull許容型には? 、null許容型ではない場合は!を使用し、nullableがデフォルトです。

取消し可能とnull許容の区別は非常に理にかなっています。 :+1:

これがぐるぐる回っているような気がします。

取消し可能/取消し不可能な定義が根本的な問題を解決しない理由を上で読んだことがありますか?

@Griffork私はすべてのコメントを読みました。 私が言ったことは、他の人が言ったことの再ハッシュ/組み合わせのようなものであることを認めますが、それが最も現実的な前進の道だと思います。 根本的な問題としてあなたが何を見ているのかわかりませんが、私にとって根本的な問題は、 nullundefinedがすべての型の一部であり、コンパイラが試行時に安全性を保証していないことですタイプT引数を使用します。 私の例のように:

`` `.ts
関数len(x:文字列):数値{
x.lengthを返します。
}

len( "works");
//エラーなし-5

len(null);
//コンパイラはこれを許可しますが、ここでは次のようなエラーが発生するはずです
//エラー:nullのプロパティ 'length'がありません

len(未定義);
//コンパイラはこれを許可しますが、ここでは次のようなエラーが発生するはずです
//エラー:未定義のプロパティ 'length'がありません
`` `

これが、_言語_の根本的な問題、つまり安全性の欠如についての私の見解です。 これの膨大な量は、静的分析と型分析を使用して関数への引数の流れを調べ、安全でないことが行われる可能性がある場合にエラーを出すことで修正できます。 コードが利用可能な場合、その分析を実行できるため、 ?Tタイプはまったく必要ありません(ただし、常に完全に正確であるとは限りません)。 ?Tタイプを追加する理由は、安全性チェックを強制し、プログラマーの意図を非常に明確にするためです。

_implementation_の問題は、下位互換性です。 TSチームが明日、型Tがデフォルトで非voidである変更をリリースした場合、それらの型シグネチャで現在void入力を受け入れて処理する既存のコードが壊れます。 TSチームのメンバーは、この問題で、このような大きな重大な変更を行うつもりはないと述べています。 効果が十分に小さく、利益が十分に大きい場合、彼らはいくつかのことを喜んで破りますが、これはあまりにも大きな影響を及ぼします。

私の提案は2つの部分に分かれており、そのうちの1つは、実際の真のバグを見つけることを除いて、既存の構文/セマンティクスを変更しない言語への素晴らしい追加になると思います。もう1つは、将来的に非void型のより強力な保証を得るために変更を壊します。 それはまだ壊滅的な変化ですが、うまくいけばもっと受け入れられるサイズと性質のものです。

パート1:

  • null / undefined / voidタイプがそれらを処理しない関数に渡される可能性がある場合を識別するための分析を追加します(定義ファイルではなく、TSコードが存在する場合にのみ機能します)。
  • 引数を使用する前にvoidチェックを強制する?Tタイプを追加します。 これは実際には、言語レベルのオプション型に関する単なる構文糖衣です。
  • これらの2つの機能は、それぞれに独自のメリットがあるため、個別に実装できます。

パート2:

  • 待って。 パート1が導入され、TSのコミュニティとユーザーがこれらの機能を使用する時間があった後、標準では、型がnullでない場合はTを使用し、 ?Tタイプがnullになる可能性がある場合は
  • 別の機能として、コンパイラオプション--noImplicitVoidを追加します。これにより、型が無効になる可能性がある場合は?T必要があります。 これは、既存のベストプラクティスを実施するコンパイラにすぎません。 ベストプラクティスに準拠していない定義ファイルがある場合、それは正しくありませんが、それがオプトインである理由です。
  • 本当に厳密にしたい場合は、フラグは、適用するディレクトリ/ファイルを指定する引数を受け入れることができます。 次に、変更をコードにのみ適用し、 node_modulesを除外することができます。

パート1は、パート2がなくても実行できるため、これが最も現実的なオプションだと思います。 これは、この問題を大幅に軽減する優れた機能です。 確かにそれは完璧ではありませんが、それが起こり得ることを意味するのであれば、私は十分にうまくいきます。 また、将来的に真の非null型のオプションをテーブルに残します。 現在の問題は、この問題が長く続くほど、TSで記述されているコードが増えるため、修正するための重大な変更が多くなることです。 パート1では、最悪の場合、それを劇的に遅くし、せいぜい時間の経過とともに影響を減らすはずです。

@tejacques私は、

@ tejacques -FWIW、私はあなたの評価と提案に完全に同意します。 TSチームが同意することを期待しましょう:)

実際には、2つのことがあります。

1つは、パート1で説明したフローのような分析が必要かどうかはわかりません。 それは非常にクールで便利ですが、私は確かにそれが無効な/ ?Tタイプを保持することを望んでいません。これは、言語の現在の設計ではるかに実現可能であり、はるかに長期的な価値を提供します。

また、 --noImplicitVoid言い回しも少し異なります。たとえば、 nullundefinedを、「無効にできる場合は?T 」。 私たちは同じことを意味していると確信しています。セマンティクスだけです。 定義ではなく使用法に焦点を当てています。TSがどのように機能するかを理解していれば、実際に適用できるのは定義だけです。

そして今、何かが頭に浮かびました。その場合、4つのレベルの無効性があります(これはフローにも当てはまります。これは、この会話の多くを刺激していると思います)。

interface Foo {
  w: string;
  x?: string;
  y: ?string;
  z?: ?string;
}

--noImplicitVoid下でwは有効な文字列のみになります。 これは型安全性にとって大きな勝利です。 さようなら、10億ドルの間違い! もちろん、 --noImplicitVoidがない場合、唯一の制約は指定する必要があるということですが、nullまたは未定義にすることができます。 これは、実際よりも多くのことを保証しているように見えるため、この言語のかなり危険な動作だと思います。

xは、現在の設定では完全に寛大です。 文字列、null、未定義の場合があり、それを実装するオブジェクトに存在する必要はありません。 --noImplicitVoid下でxundefined (模倣非存在を)ではなく、 null 。 ただし、これはTypeScriptには少し意見が多すぎるかもしれません。

また、使用する前にx無効にすることを要求することは論理的に理にかなっていますが、これもまた重大な変更になります。 この動作は--noImplicitVoidフラグの一部である可能性がありますか?

yは任意の文字列またはvoid値に設定できますが、voidであっても、何らかの形式で存在する必要があります。 そしてもちろん、それにアクセスするにはボイドチェックが必要です。 この動作は少し意外かもしれません。 undefined設定するのと同じに設定しないことを検討する必要がありますか? もしそうなら、無効チェックを要求することを除いて、それをxと何が違うのでしょうか?

そして最後に、 z指定する必要はなく、絶対に何にでも設定でき(もちろん、文字列以外を除く)、(正当な理由で)アクセスする前にvoidチェックが必要です。 ここではすべてが理にかなっています!

xy間には少し重複があり、 xは最終的にコミュニティによって非推奨になり、安全性を最大化するためにzフォームを優先するようになると思います。 。

@tejacques

コードが利用可能な場合、その分析を実行できるため、?Tタイプはまったく必要ありません(ただし、常に完全に正確であるとは限りません)。

このステートメントは正しくありません。 完全なコードソースが利用可能であっても、null可能性の多くの側面を説明することはできません。

たとえば、インターフェースを(静的に)呼び出す場合、インターフェースに適切な注釈が付けられていない場合、nullを渡しても問題がないかどうかを知ることはできません。 TSのような構造型システムでは、どのオブジェクトがインターフェースの実装であり、どのオブジェクトが実装されていないかを知ることさえ簡単ではありません。 一般的には気にしませんが、ソースコードからnull可能性を推測したい場合はそうします。

別の例:配列listと関数unsafe(x)あり、そのソースコードがnull引数を受け入れないことを示している場合、コンパイラはそれを判断できません。行が安全かどうか: list.filter(unsafe) 。 実際、 listすべての可能なコンテンツが何であるかを静的に知ることができない限り、これを行うことはできません。

その他のケースは、継承などに関連しています。

nullコントラクトの露骨な違反にフラグを立てるコード分析ツールに価値がないと言っているのではありません(そうです)。 ソースコードが利用可能なときにnullアノテーションの有用性を軽視することは、私見ではエラーであることを指摘しているだけです。

この議論のどこかで、null可能性の推論は、多くの単純なケースで下位互換性を減らすのに役立つと思うと言いました。 ただし、ソース注釈を完全に置き換えることはできません。

@tejacques私の悪い、私はあなたのコメントを読み間違えました(何らかの理由で私の脳はvoid===null :(私はただ起きていると非難します)。
余分な投稿をありがとう、それはあなたの元の投稿よりもはるかに理にかなっています。 私は実際にそのアイデアがとても好きです。

1つの投稿に書き出すのは判読できないブロブなので、3人に別々に返信します。

@Griffork問題ありません。 テキストですべてを正しく伝えるのは難しい場合があり、とにかく明確にする価値があったと思います。

@dallonfあなたの言い換えは、まさに私が言っていることです-私たちは同じページにいます。

x?: Ty: ?Tの違いは、ツールチップ/関数の使用法と、使用されるタイピングとガードにあると思います。

オプションの引数として宣言すると、ツールチップ/関数の使用法が変更されるため、オプションであることは明らかです。
a?: Tは、関数呼び出しで引数として渡す必要はありません。空白のままにすることができます
宣言に?:がない場合は、関数呼び出しで引数として渡す必要があり、空白のままにすることはできません。

w: Tは必須の非void引数です( --noImplicitVoid
警備員は必要ありません

x?: Tはオプションの引数であるため、型は実際にはT | undefined
if (typeof x !== 'undefined')ガードが必要です。
トリプルグリフ注意!==上の正確なチェックのためのundefined

y: ?Tは必須の引数であり、型は実際にはT | void
if (y == null)ガードが必要です。
nullundefined両方に一致する二重グリフ== undefinedつまりvoid注意してください。

z?: ?Tはオプションの引数であり、型は実際にはT | undefined | voidあり、これはT | void
if (z == null)ガードが必要です。
nullundefined両方に一致する二重グリフ== undefinedつまりvoid再度注意してください。

すべてのオプションの引数と同様に、オプションの引数の後に必須の引数を設定することはできません。 それが違いです。 これで、オプションの引数に対してnullガードを実行することもできます。これも機能しますが、重要な違いは、 null値を関数に渡せないことです。それを呼んだ; ただし、 undefined渡すことはできます。

これらすべてに実際に場所があると思うので、必ずしも現在のオプションの引数構文を廃止するわけではありませんが、 z形式が最も安全であることに同意します。

編集:文言を更新し、いくつかのタイプミスを修正しました。

@ jods4私はあなたが言ったことのほとんどすべてに同意します。 voidタイピングの重要性を軽視しようとはしていません。 一度に1フェーズずつプッシュしようとしています。 TSチームが後でそれを行うことができない場合は、少なくとも私たちのほうがよいでしょう。さらにチェックと?T実装した後、TSチームがそれを行うことができれば、それが使命です。

Arraysは、実に難しい問題だと思います。 あなたはいつでもこのようなことをすることができます:

`` `.ts
関数numToString(x:数値){
x.toString();を返します。
}
var nums:number [] = Array(100);
numToString(nums [0]); // お前、やっちゃったんだな!

You can try to do something specifically for uninitialized arrays, like typing the `Array` function as `Array<?T>` / `?T[]` and upgrading it to `T[]` after a for-loop initializing it, but I agree that you can't catch everything. That said, that's already a problem anyway, and arrays typically don't even send uninitialized values to `map`/`filter`/`forEach`.

Here's an example -- the output is the same on Node/Chrome/IE/FF/Safari.

``` .ts
function timesTwo(x: number) {
    return x * 2;
}
function all(x) {
    return true;
}
var nums: number[] = Array(100);
nums.map(timesTwo);
// [undefined x 100]
nums.filter(all);
// []
nums.forEach(function(x) { console.log(x); })
// No output

予想外のことなので、それほど役に立ちませんが、今日の実際のJavaScriptではエラーではありません。

私が強調したい他の唯一のことは、インターフェースでも進歩できるということです。型システムよりも静的分析の方がはるかに多くの作業と労力を要しますが、現在すでに行われていることとそれほど変わりません。

これが例です。 --noImplicitVoidがオフになっていると仮定しましょう

`` `.ts
インターフェイスITransform{{
(x:T):U;
}

インターフェイスIHaveName {
名前:文字列;
}

関数変換(x:T、fn:ITransform){
fn(x);を返します。
}

名前付き変数= {
名前:「Foo」
};

varwrongName = {
名前:1234
};

var namedNull = {
名前:null
};

var someFun =(x:IHaveName)=> x.name;
var someFunHandlesVoid =(x:IHaveName)=> {
if(x!= null && x.name!= null){
x.nameを返します。
}
「名前なし」を返します。
};

All of the above code compiles just fine -- no issues. Now let's try using it

``` .ts
someFun(named);
// "Foo"
someFun(wrongName);
// error TS2345: Argument of type '{ name: number; }' is not assignable to parameter
// of type 'IHaveName'.
//   Types of property 'name' are incompatible.
//     Type 'number' is not assignable to type 'string'.
someFun(null);
// Not currently an error, but would be something like this:
// error TS#: Argument of type 'null' is not assignale to parameter of type 'IHaveName'.
someFun(namedNull);
// Not currently an error, but would be something like this:
// error TS#: Argument of type '{ name: null; }' is not assignable to parameter of
// type 'IHaveName'.
//   Types of property 'name' are incompatible.
//     Type 'null' is not assignable to type 'string'.

someFunHandlesVoid(named);
// "Foo"
someFunHandlesVoid(wrongName);
// error TS2345: Argument of type '{ name: number; }' is not assignable to parameter
// of type 'IHaveName'.
someFunHandlesVoid(null);
// "No Name"
someFunHandlesVoid(namedNull);
// "No Name"

transform(named, someFun);
// "Foo"
transform(wrongName, someFun);
// error TS2453: The type argument for type parameter 'T' cannot be inferred from the usage.
// Consider specifying the type arguments explicitly.
//   Type argument candidate '{ name: number; }' is not a valid type argument because it
//   is not a supertype of candidate 'IHaveName'.
//     Types of property 'name' are incompatible.
//       Type 'string' is not assignable to type 'number'.
transform(null, someFun);
// Not currently an error, but would be something like this:
// error TS#: The type argument for type parameter 'T' cannot be inferred from the usage.
// Consider specifying the type arguments explicitly.
//   Type argument candidate 'null' is not a valid type argument because it
//   is not a supertype of candidate 'IHaveName'.
transform(namedNull, someFun);
// Not currently an error, but would be something like this:
// error TS#: The type argument for type parameter 'T' cannot be inferred from the usage.
// Consider specifying the type arguments explicitly.
//   Type argument candidate '{ name: null; }' is not a valid type argument because it
//   is not a supertype of candidate 'IHaveName'.
//     Types of property 'name' are incompatible.
//       Type 'string' is not assignable to type 'null'.

transform(named, someFunHandlesVoid);
// "Foo"
transform(wrongName, someFunHandlesVoid);
// error TS2453: The type argument for type parameter 'T' cannot be inferred from the usage.
// Consider specifying the type arguments explicitly.
//   Type argument candidate '{ name: number; }' is not a valid type argument because it
//   is not a supertype of candidate 'IHaveName'.
transform(null, someFunHandlesVoid);
// "No Name"
transform(namedNull, someFunHandlesVoid);
// "No Name"

すべてを捕まえることはできませんが、たくさんのものを捕まえることはできます。

最後の注意- --noImplicitVoidがオンの場合、上記の動作はどうなりますか?

これで、 someFunsomeFunHandlesVoid両方が同じタイプチェックされ、 someFunが生成したのと同じエラーメッセージが生成されます。 someFunHandlesVoidはvoidを処理しますが、 nullまたはundefinedで呼び出すと、署名にvoid以外の時間がかかると記載されているため、エラーになります。 nullまたはundefinedを受け入れるにnull(x: ?IHaveName) : stringと入力する必要があります。 タイプを変更しても、以前と同じように機能し続けます。

これは重大な変更である部分ですが、それを修正するために必要なのは、型署名に1文字の?を追加することだけでした。 警告と同じことを行う別のフラグ--warnImplicitVoidを設定して、ゆっくりと移行することもできます。

私はこれを行うための完全なジャークのように感じますが、私はもう1つの投稿をするつもりです。

この時点で、先に進むために何をすべきかわかりません。 より良いアイデアはありますか? 我々がすべき:

  • これがどのように振る舞うべきかについて話し合い/推測し続けますか?
  • これを3つの新機能の提案に変えますか?

    • 強化された分析

    • 多分/オプションタイプ?T

    • --noImplicitVoidコンパイラオプション

  • TypeScriptチームメンバーに入力を求めますか?

TypeScriptチームにこのスレッドの長さを考慮して追いつくように依頼するのはほとんど非人道的であるため、私は新しい提案に傾倒し、そこで議論を続けています。

@tejacques

  • トリプルイコールの例でダロンフにtypeofがありません。
  • jods4の例では、 ?が欠落しているようです。

ものはこのスレッドにとどまる必要があると思うのと同じように、このスレッドは実際にはもう「監視」されていないと思います(たまにちらっと見ているようなものかもしれません)。 したがって、いくつかの新しいスレッドを作成すると、確実に牽引力が構築されます。
しかし、人々が頭を上げて最初にフィードバックを提供するまで、数日/週待ってください。 あなたはあなたの提案がかなりしっかりしていることを望むでしょう。

編集:記憶されたマークダウンが存在します。

このスレッドにコメントすることは、この段階ではかなり無駄です。 TypeScriptチームが許容できると考える提案がなされたとしても(私は8月にこれを試みました)、彼らがそれをノイズの中に見つける方法はありません。

あなたが望むことができる最善のことは、注意のレベルが、TypeScriptチームが独自の提案を考え出し、それを実装するための十分なプロンプトであるということです。 それ以外の場合は、それを忘れてFlowを使用してください。

これを分割するための+1ですが、今のところ、 --noImplicitVoidオプションは
null許容型が実装されるのを待ちます。

これまでのところ、構文とセマンティクスについてはほとんど合意に達しています。
null許容型なので、誰かが提案と実装を書き出すことができれば
そのうち、それは金色になります。 同様のプロセスからの提案があります
他のタイプの列挙型に関しては、しかし私には時間がありませんでした
他のプロジェクトのためにそれを実装します。

水曜日、2015年11月18日には、午前21時24トムジャックの[email protected]書きました:

私はこれをするための完全なジャークのように感じますが、私はもう1つ作るつもりです
役職。

この時点で、先に進むために何をすべきかわかりません。 より良いアイデアはありますか?
我々がすべき:

  • これがどのように振る舞うべきかについて話し合い/推測し続けますか?
  • これを3つの新機能の提案に変えますか?

    • 強化された分析

    • 多分/オプションタイプ?T

    • --noImplicitVoidコンパイラオプション

  • TypeScriptチームメンバーに入力を求めますか?

私は新しい提案に傾倒し、それ以来そこで議論を続けています
TypeScriptチームにこのスレッドに追いつくように頼むのはほとんど非人道的です
それがどれくらいの長さかを考えると。


このメールに直接返信するか、GitHubで表示してください
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -157928828

--noImplicitNullオプションの場合は+1(voidおよびnullの割り当てを禁止)。

特殊なタイプOp<A> = A | NullTypeして、この問題を軽減しようとしました。 それはかなりうまくいくようです。 ここを参照して

_-- noImplicitNull_の場合も+1してください:+1:

--noImplicitNullの+1

これを閉じる必要がありますか?

@Gaelan Gived #7140がマージされました。ここで数人の人が提案したように、 --noImplicitNull新しい専用の問題を提出したい場合は、今すぐ安全に行うことができます。

@isiahmeadowsそのときは、これを開いたままにしておく方がよいでしょう。

これを閉じる必要がありますか?

https://github.com/Microsoft/TypeScript/issues/2388は、この作業の名前変更部分であると考えてい

ここで数人の人が提案したように、 --noImplicitNull新しい専用の問題を提出したい場合は、今すぐ提出するのがおそらく安全です。

この新しいフラグのセマンティクスが何を要求されているのか理解できません。 明確な提案で新しい問題を開くことをお勧めします。

@mhegazyこの号の前半で--noImplicitNullについて提起されたアイデアは、すべてが明示的に?Typeまたは!Typeなければならないというもの

#7140と#8010の両方がマージされたので終了します。

クローズされた問題についてコメントした場合は申し訳ありませんが、どこに質問すればよいかわかりません。興味がなければ、これは新しい問題の価値があるとは思いません。
ファイルごとに暗黙のnullを処理することは可能でしょうか?
同様に、noImplicitNullを使用して多数のtdファイルを処理しますが(それらは間違いなく型指定されており、そのように考案されているため)、ソースをimplicitNullとして処理しますか?
誰かがこれが役に立つと思いますか?

@ massimiliano-mantione、 https://github.com/Microsoft/TypeScript/issues/8405を参照して

このページは役に立ちましたか?
0 / 5 - 0 評価

関連する問題

fwanicka picture fwanicka  ·  3コメント

jbondc picture jbondc  ·  3コメント

Antony-Jones picture Antony-Jones  ·  3コメント

siddjain picture siddjain  ·  3コメント

dlaberge picture dlaberge  ·  3コメント